aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorcc4b05f61e2d8f77114750386c9f9a60 <cc4b05f61e2d8f7@7114750386c9f9a60>2023-05-11 14:38:47 +0000
committercc4b05f61e2d8f77114750386c9f9a60 <cc4b05f61e2d8f7@7114750386c9f9a60>2023-05-11 14:38:47 +0000
commitf5450bfd35a6410528d124f534c2b1a958cafe51 (patch)
treea808b12d6ad5343fabdec7b8918df6b4d844e03f /src
parent5ad2bb7a6ac7e97c031908d2439808a00fff6214 (diff)
downloaddmsdosnow-dmsdos-0.9.2.2.tar.gz
dmsdos-0.9.2.2 addeddmsdos-0.9.2.2
Diffstat (limited to 'src')
-rw-r--r--src/Config.in82
-rw-r--r--src/Configure438
-rw-r--r--src/Configure.help365
-rw-r--r--src/Makefile242
-rw-r--r--src/Makefile.cygwinb20103
-rw-r--r--src/Makefile.kernel54
-rw-r--r--src/Makefile.module28
-rw-r--r--src/Makefile.old215
-rw-r--r--src/Menuconfig1187
-rw-r--r--src/cctest.c14
-rw-r--r--src/check.sh92
-rw-r--r--src/cmpdisk.README3
-rw-r--r--src/cmpdisk.cpp131
-rw-r--r--src/conf-wrapper.sh14
-rw-r--r--src/config.debug62
-rw-r--r--src/config.high-traffic-write64
-rw-r--r--src/config.low-traffic-write64
-rw-r--r--src/config.small-module63
-rw-r--r--src/cvflist.c264
-rw-r--r--src/cvftest.c120
-rw-r--r--src/daemon_actions.c301
-rw-r--r--src/dblspace_alloc.c710
-rw-r--r--src/dblspace_buffer.c195
-rw-r--r--src/dblspace_chk.c559
-rw-r--r--src/dblspace_compr.c711
-rw-r--r--src/dblspace_dec.c671
-rw-r--r--src/dblspace_fileops.c42
-rw-r--r--src/dblspace_fileops.c-2.0.33660
-rw-r--r--src/dblspace_fileops.c-2.1.80722
-rw-r--r--src/dblspace_fileops.c-2.3.10563
-rw-r--r--src/dblspace_interface.c1147
-rw-r--r--src/dblspace_ioctl.c971
-rw-r--r--src/dblspace_methsq.c1256
-rw-r--r--src/dblspace_tables.c760
-rw-r--r--src/dblspace_virtual.c989
-rw-r--r--src/dcread.c344
-rw-r--r--src/dmsdos-config.default65
-rw-r--r--src/dmsdos-config.h.default24
-rw-r--r--src/dmsdos.h716
-rw-r--r--src/dmsdosfsck.c674
-rw-r--r--src/dstacker_alloc.c477
-rw-r--r--src/dstacker_compr.c1234
-rw-r--r--src/dstacker_dec.c824
-rw-r--r--src/dutil.c419
-rw-r--r--src/lib_interface.c726
-rw-r--r--src/lib_interface.h189
-rw-r--r--src/listmsg.sh21
-rw-r--r--src/mcdmsdos.c409
-rw-r--r--src/msdos.fsck-wrapper126
-rw-r--r--src/msdos.fsck-wrapper2101
-rw-r--r--src/my_break.h30
-rw-r--r--src/prepare_dos_8.327
-rw-r--r--src/putdos18
-rw-r--r--src/putdos.cfg19
-rw-r--r--src/putdos_helper14
-rw-r--r--src/remove_comments.awk36
-rw-r--r--src/win32_msc50.bat65
57 files changed, 20420 insertions, 0 deletions
diff --git a/src/Config.in b/src/Config.in
new file mode 100644
index 0000000..a707e2f
--- /dev/null
+++ b/src/Config.in
@@ -0,0 +1,82 @@
+#
+# For a description of the syntax of this configuration file,
+# see the Configure script.
+#
+mainmenu_name "Dmsdos Compile-Time Configuration"
+
+bool 'Detailed DMSDOS configuration (expert mode)' DMSDOS_EXPERT
+
+mainmenu_option next_comment
+comment 'CVF formats to be supported'
+bool 'Dos 6.0-6.22/Win95 Doublespace/Drivespace' DMSDOS_CONFIG_DBLSP_DRVSP
+bool 'Win95 Drivespace 3' DMSDOS_CONFIG_DRVSP3
+bool 'Stacker version 3' DMSDOS_CONFIG_STAC3
+bool 'Stacker version 4' DMSDOS_CONFIG_STAC4
+endmenu
+
+if [ "$DMSDOS_EXPERT" = "y" ]; then
+mainmenu_option next_comment
+comment 'Memory Management'
+bool 'Enable advanced memory management (only for 2.0.xx kernels)' USE_XMALLOC
+if [ "$USE_XMALLOC" = "n" ]; then
+ bool 'Use vmalloc instead of kmalloc for cluster cache' USE_VMALLOC
+fi
+endmenu
+
+mainmenu_option next_comment
+comment 'Cache setup'
+int 'Cache size for delayed compression (in clusters)' LISTSIZE
+int 'Maximum number of buffers in MDFAT cache' MDFATCACHESIZE
+int 'Maximum number of buffers in DFAT cache' DFATCACHESIZE
+int 'Maximum number of buffers in BITFAT cache' BITFATCACHESIZE
+int 'Idle buffer timeout (in seconds)' MAX_CACHE_TIME
+int 'Cache size for normal operation (in clusters)' CCACHESIZE
+int 'Idle cluster timeout (in seconds)' MAX_CCACHE_TIME
+endmenu
+
+mainmenu_option next_comment
+comment 'Read-ahead Options'
+int 'Maximum number of blocks to read ahead' MAX_READA
+bool 'Enable advanced read-ahead' USE_READA_LIST
+if [ "$USE_READA_LIST" = "y" ]; then
+ int 'Read-ahead queue size in blocks' READA_LIST_SIZE
+fi
+int 'Read-ahead threshold' READA_THRESHOLD
+endmenu
+fi
+
+mainmenu_option next_comment
+comment 'Misc Options'
+bool 'Write access support' DBL_WRITEACCESS
+bool 'Disable logging completely' NOLOG
+if [ "$DMSDOS_EXPERT" = "y" ]; then
+int 'Default loglevel' DEFAULT_LOGLEVEL
+int 'Default compression level' DEFAULT_CF
+bool 'Sequence number logging' SEQLOG
+bool 'Writable mmap support (always enable this for 2.1/2.2 kernels)' DMSDOS_USE_READPAGE
+fi
+endmenu
+
+mainmenu_option next_comment
+comment 'Internal Compression Daemon'
+
+bool 'Enable internal compression daemon' INTERNAL_DAEMON
+if [ "$DMSDOS_EXPERT" = "y" -a "$INTERNAL_DAEMON" != "n" ]; then
+ int 'Daemon wakeup interval (in seconds)' IDMSDOSD_TIME
+fi
+endmenu
+
+if [ "$DMSDOS_EXPERT" = "y" ]; then
+mainmenu_option next_comment
+comment 'Speedup Tricks & Hacks'
+bool 'Leave directories uncompressed' SP_BIT0
+bool 'Leave umsdos EMD uncompressed' SP_BIT1
+bool 'Skip exact search in BITFAT allocation' SP_BIT2
+bool 'Fast unmount' SP_BIT3
+bool 'Write-back cluster caching (instead of write-through)' SP_BIT4
+bool 'Enable read-ahead prediction' SP_BIT5
+bool 'Fast BITFAT allocation (risky)' SP_BIT6
+bool 'Use delayed compression (with dmsdos daemon)' SP_BIT7
+bool 'Avoid fragmented writes' SP_BIT8
+endmenu
+fi
diff --git a/src/Configure b/src/Configure
new file mode 100644
index 0000000..f080f42
--- /dev/null
+++ b/src/Configure
@@ -0,0 +1,438 @@
+#! /bin/sh
+#
+# This script is used to configure dmsdos compile-time options.
+# It has been shamelessly stolen from the linux kernel. For a list
+# of credits see there (linux/scripts/Configure).
+#
+
+#
+# Make sure we're really running bash.
+#
+# I would really have preferred to write this script in a language with
+# better string handling, but alas, bash is the only scripting language
+# that I can be reasonable sure everybody has on their linux machine.
+#
+[ -z "$BASH" ] && { echo "Configure requires bash" 1>&2; exit 1; }
+
+# Disable filename globbing once and for all.
+# Enable function cacheing.
+set -f -h
+
+#
+# Dummy functions for use with a config.in modified for menuconf
+#
+function mainmenu_option () {
+ :
+}
+function mainmenu_name () {
+ :
+}
+function endmenu () {
+ :
+}
+
+#
+# help prints the corresponding help text from Configure.help to stdout
+#
+# help variable
+#
+function help () {
+ if [ -f Configure.help ]
+ then
+ #first escape regexp special characters in the argument:
+ var=$(echo "$1"|sed 's/[][\/.^$*]/\\&/g')
+ #now pick out the right help text:
+ text=$(sed -n "/^$var[ ]*\$/,\${
+ /^$var[ ]*\$/b
+ /^#.*/b
+ /^[ ]*\$/q
+ p
+ }" Configure.help)
+ if [ -z "$text" ]
+ then
+ echo; echo " Sorry, no help available for this option yet.";echo
+ else
+ (echo; echo "$text"; echo) | ${PAGER:-more}
+ fi
+ else
+ echo;
+ echo " Can't access the file Configure.help which"
+ echo " should contain the help texts."
+ echo
+ fi
+}
+
+
+#
+# readln reads a line into $ans.
+#
+# readln prompt default oldval
+#
+function readln () {
+ if [ "$DEFAULT" = "-d" -a -n "$3" ]; then
+ echo "$1"
+ ans=$2
+ else
+ echo -n "$1"
+ [ -z "$3" ] && echo -n "(NEW) "
+ IFS='@' read ans </dev/tty || exit 1
+ [ -z "$ans" ] && ans=$2
+ fi
+}
+
+#
+# comment does some pretty-printing
+#
+# comment 'xxx'
+#
+function comment () {
+ echo "*"; echo "* $1" ; echo "*"
+ (echo "" ; echo "#"; echo "# $1" ; echo "#") >>$CONFIG
+ (echo "" ; echo "/*"; echo " * $1" ; echo " */") >>$CONFIG_H
+}
+
+#
+# define_bool sets the value of a boolean argument
+#
+# define_bool define value
+#
+function define_bool () {
+ case "$2" in
+ "y")
+ echo "$1=y" >>$CONFIG
+ echo "#define $1 1" >>$CONFIG_H
+ ;;
+
+ "m")
+ echo "$1=m" >>$CONFIG
+ echo "#undef $1" >>$CONFIG_H
+ echo "#define $1_MODULE 1" >>$CONFIG_H
+ ;;
+
+ "n")
+ echo "# $1 is not set" >>$CONFIG
+ echo "#undef $1" >>$CONFIG_H
+ ;;
+ esac
+ eval "$1=$2"
+}
+
+#
+# bool processes a boolean argument
+#
+# bool question define
+#
+function bool () {
+ old=$(eval echo "\${$2}")
+ def=${old:-'n'}
+ case "$def" in
+ "y" | "m") defprompt="Y/n/?"
+ def="y"
+ ;;
+ "n") defprompt="N/y/?"
+ ;;
+ esac
+ while :; do
+ readln "$1 ($2) [$defprompt] " "$def" "$old"
+ case "$ans" in
+ [yY] | [yY]es ) define_bool "$2" "y"
+ break;;
+ [nN] | [nN]o ) define_bool "$2" "n"
+ break;;
+ * ) help "$2"
+ ;;
+ esac
+ done
+}
+
+#
+# tristate processes a tristate argument
+#
+# tristate question define
+#
+function tristate () {
+ if [ "$CONFIG_MODULES" != "y" ]; then
+ bool "$1" "$2"
+ else
+ old=$(eval echo "\${$2}")
+ def=${old:-'n'}
+ case "$def" in
+ "y") defprompt="Y/m/n/?"
+ ;;
+ "m") defprompt="M/n/y/?"
+ ;;
+ "n") defprompt="N/y/m/?"
+ ;;
+ esac
+ while :; do
+ readln "$1 ($2) [$defprompt] " "$def" "$old"
+ case "$ans" in
+ [yY] | [yY]es ) define_bool "$2" "y"
+ break ;;
+ [nN] | [nN]o ) define_bool "$2" "n"
+ break ;;
+ [mM] ) define_bool "$2" "m"
+ break ;;
+ * ) help "$2"
+ ;;
+ esac
+ done
+ fi
+}
+
+#
+# dep_tristate processes a tristate argument that depends upon
+# another option. If the option we depend upon is a module,
+# then the only allowable options are M or N. If Y, then
+# this is a normal tristate. This is used in cases where modules
+# are nested, and one module requires the presence of something
+# else in the kernel.
+#
+# tristate question define default
+#
+function dep_tristate () {
+ old=$(eval echo "\${$2}")
+ def=${old:-'n'}
+ if [ "$3" = "n" ]; then
+ define_bool "$2" "n"
+ elif [ "$3" = "y" ]; then
+ tristate "$1" "$2"
+ else
+ if [ "$CONFIG_MODULES" = "y" ]; then
+ case "$def" in
+ "y" | "m") defprompt="M/n/?"
+ def="m"
+ ;;
+ "n") defprompt="N/m/?"
+ ;;
+ esac
+ while :; do
+ readln "$1 ($2) [$defprompt] " "$def" "$old"
+ case "$ans" in
+ [nN] | [nN]o ) define_bool "$2" "n"
+ break ;;
+ [mM] ) define_bool "$2" "m"
+ break ;;
+ [yY] | [yY]es ) echo
+ echo " This answer is not allowed, because it is not consistent with"
+ echo " your other choices."
+ echo " This driver depends on another one which you chose to compile"
+ echo " as a module. This means that you can either compile this one"
+ echo " as a module as well (with M) or leave it out altogether (N)."
+ echo
+ ;;
+ * ) help "$2"
+ ;;
+ esac
+ done
+ fi
+ fi
+}
+
+#
+# define_int sets the value of a integer argument
+#
+# define_int define value
+#
+function define_int () {
+ echo "$1=$2" >>$CONFIG
+ echo "#define $1 ($2)" >>$CONFIG_H
+ eval "$1=$2"
+}
+
+#
+# int processes an integer argument
+#
+# int question define default
+# GNU expr changed handling of ?. In older versions you need ?,
+# in newer you need \?
+OLD_EXPR=`expr "0" : '0\?'`
+if [ $OLD_EXPR -eq 1 ]; then
+ INT_EXPR='0$\|-\?[1-9][0-9]*$'
+else
+ INT_EXPR='0$\|-?[1-9][0-9]*$'
+fi
+function int () {
+ old=$(eval echo "\${$2}")
+ def=${old:-$3}
+ while :; do
+ readln "$1 ($2) [$def] " "$def" "$old"
+ if expr "$ans" : $INT_EXPR > /dev/null; then
+ define_int "$2" "$ans"
+ break
+ else
+ help "$2"
+ fi
+ done
+}
+#
+# define_hex sets the value of a hexadecimal argument
+#
+# define_hex define value
+#
+function define_hex () {
+ echo "$1=$2" >>$CONFIG
+ echo "#define $1 0x${2#*[x,X]}" >>$CONFIG_H
+ eval "$1=$2"
+}
+
+#
+# hex processes an hexadecimal argument
+#
+# hex question define default
+#
+function hex () {
+ old=$(eval echo "\${$2}")
+ def=${old:-$3}
+ def=${def#*[x,X]}
+ while :; do
+ readln "$1 ($2) [$def] " "$def" "$old"
+ ans=${ans#*[x,X]}
+ if expr "$ans" : '[0-9a-fA-F][0-9a-fA-F]*$' > /dev/null; then
+ define_hex "$2" "$ans"
+ break
+ else
+ help "$2"
+ fi
+ done
+}
+
+#
+# choice processes a choice list (1-out-of-n)
+#
+# choice question choice-list default
+#
+# The choice list has a syntax of:
+# NAME WHITESPACE VALUE { WHITESPACE NAME WHITESPACE VALUE }
+# The user may enter any unique prefix of one of the NAMEs and
+# choice will define VALUE as if it were a boolean option.
+# VALUE must be in all uppercase. Normally, VALUE is of the
+# form CONFIG_<something>. Thus, if the user selects <something>,
+# the CPP symbol CONFIG_<something> will be defined and the
+# shell variable CONFIG_<something> will be set to "y".
+#
+function choice () {
+ question="$1"
+ choices="$2"
+ old=
+ def=$3
+
+ # determine default answer:
+ names=""
+ set -- $choices
+ firstvar=$2
+ while [ -n "$2" ]; do
+ if [ -n "$names" ]; then
+ names="$names, $1"
+ else
+ names="$1"
+ fi
+ if [ "$(eval echo \"\${$2}\")" = "y" ]; then
+ old=$1
+ def=$1
+ fi
+ shift; shift
+ done
+
+ val=""
+ while [ -z "$val" ]; do
+ ambg=n
+ readln "$question ($names) [$def] " "$def" "$old"
+ ans=$(echo $ans | tr a-z A-Z)
+ set -- $choices
+ while [ -n "$1" ]; do
+ name=$(echo $1 | tr a-z A-Z)
+ case "$name" in
+ "$ans"* )
+ if [ "$name" = "$ans" ]; then
+ val="$2"
+ break # stop on exact match
+ fi
+ if [ -n "$val" ]; then
+ echo;echo \
+ " Sorry, \"$ans\" is ambiguous; please enter a longer string."
+ echo
+ val=""
+ ambg=y
+ break
+ else
+ val="$2"
+ fi;;
+ esac
+ shift; shift
+ done
+ if [ "$val" = "" -a "$ambg" = "n" ]; then
+ help "$firstvar"
+ fi
+ done
+ set -- $choices
+ while [ -n "$2" ]; do
+ if [ "$2" = "$val" ]; then
+ echo " defined $val"
+ define_bool "$2" "y"
+ else
+ define_bool "$2" "n"
+ fi
+ shift; shift
+ done
+}
+
+CONFIG=.tmpconfig
+CONFIG_H=.tmpconfig.h
+trap "rm -f $CONFIG $CONFIG_H ; exit 1" 1 2
+
+#
+# Make sure we start out with a clean slate.
+#
+echo "#" > $CONFIG
+echo "# Automatically generated make config: don't edit" >> $CONFIG
+echo "#" >> $CONFIG
+
+echo "/*" > $CONFIG_H
+echo " * Automatically generated C config: don't edit" >> $CONFIG_H
+echo " * Run Configure instead" >> $CONFIG_H
+echo " */" >> $CONFIG_H
+
+DEFAULT=""
+if [ "$1" = "-d" ] ; then
+ DEFAULT="-d"
+ shift
+fi
+
+CONFIG_IN=./Config.in
+if [ "$1" != "" ] ; then
+ CONFIG_IN=$1
+fi
+
+DEFAULTS=dmsdos-config.default
+if [ -f .config ]; then
+ DEFAULTS=.config
+fi
+
+if [ -f $DEFAULTS ]; then
+ echo "#"
+ echo "# Using defaults found in" $DEFAULTS
+ echo "#"
+ . $DEFAULTS
+ sed -e 's/# \(.*\) is not.*/\1=n/' < $DEFAULTS > /tmp/conf.$$
+ . /tmp/conf.$$
+ rm /tmp/conf.$$
+else
+ echo "#"
+ echo "# No defaults found"
+ echo "#"
+fi
+
+. $CONFIG_IN
+
+rm -f .config.old
+if [ -f .config ]; then
+ mv .config .config.old
+fi
+mv .tmpconfig .config
+mv .tmpconfig.h dmsdos-config.h
+
+echo
+echo "Dmsdos is now configured. Do a 'make clean; make' to (re)compile."
+
+exit 0
diff --git a/src/Configure.help b/src/Configure.help
new file mode 100644
index 0000000..fca412f
--- /dev/null
+++ b/src/Configure.help
@@ -0,0 +1,365 @@
+# help file for dmsdos configuration
+# idea shamelessly stolen from Linux kernel
+
+# Format of this file: description<nl>variable<nl>helptext<nl><nl>.
+# If the question being documented is of type "choice", we list
+# only the first occurring config variable. The help texts
+# must not contain empty lines. No variable should occur twice; if it
+# does, only the first occurrence will be used by Configure. The lines
+# in a help text should be indented two positions. Lines starting with
+# #' are ignored. To be nice to menuconfig, limit your lines to 70
+# characters.
+
+Detailed DMSDOS configuration (expert mode)
+DMSDOS_EXPERT
+ Say Y if you want a detailed setup (then a lot of things that you
+ may not be familiar with unless you are a dmsdos expert will be
+ asked). If you say N most dmsdos internal values are set to safe
+ defaults which should be sufficient for most systems. (Yes, you
+ _can_ change everything. But you'll have to understand what you do.)
+
+Dos 6.0-6.22/Win95 Doublespace/Drivespace
+DMSDOS_CONFIG_DBLSP_DRVSP
+ Say Y if you want support for this kind of CVFs. This kind of CVF
+ format is built into MSDOS 6.0-6.22 and older versions of Win95.
+ If unsure, saying Y won't hurt except making the module larger.
+
+Win95 Drivespace 3
+DMSDOS_CONFIG_DRVSP3
+ Say Y if you want support for this kind of CVFs. Note that
+ Drivespace 3 has been sold seperately from Win95 for some time, but
+ newer versions of Win95 seem to have Drivespace 3 built in.
+ The software should tell its version number in the Help/Info menu
+ under Win95. If unsure, saying Y won't hurt except making the module
+ larger.
+
+Stacker version 3
+DMSDOS_CONFIG_STAC3
+ Say Y if you want support for this kind of CVFs. If unsure, saying Y
+ won't hurt except making the module larger.
+
+Stacker version 4
+DMSDOS_CONFIG_STAC4
+ Say Y if you want support for this kind of CVFs. If unsure, saying Y
+ won't hurt except making the module larger.
+
+Use vmalloc instead of kmalloc
+USE_VMALLOC
+ The kernel knows to memory allocation interfaces - kmalloc and
+ vmalloc. kmalloc is faster but more likely to fail (i.e. claim that
+ the system is out of memory) - vmalloc very rarely fails but memory
+ is swappable and it is surely not a good idea to swap the cluster
+ cache to the disk...
+ If you have a system with lots of physical RAM you might want to
+ try kmalloc for better performance. vmalloc is always the safe
+ solution. If you are not sure, say Y, which is always safe. YOU ARE
+ STRONLY ENCOURAGED TO ENABLE VMALLOC WHEN DEBUGGING NEW CODE.
+
+Advanced memory allocation
+USE_XMALLOC
+ Enables optimized memory allocation routine - it decides itself
+ whether to use kmalloc or vmalloc depending on the requested size
+ and on what the system can provide.
+ WARNING: this does not work for all kernels. It is known to work
+ for 2.0.xx kernels. But I've got reports about problems with some
+ 2.1.XX kernels and xmalloc. Maybe it also fails under 2.2.x. If
+ you get a message (maybe even a kernel panic) saying "please
+ disable XMALLOC" it is broken under your kernel.
+ You may receive *lots* of "Couldn't get a free page" messages in
+ the syslog which can be safely ignored - they result from the
+ driver's memory allocation strategy.
+ DON'T USE XMALLOC WHEN DEBUGGING NEW CODE.
+ Say N to feel safe.
+
+Size of daemon cache
+LISTSIZE
+ Maximum number of clusters that can be delayed for compression -
+ this is for the dmsdos compression daemon, not for write-back
+ caching. A value larger than 4096 seems to slow down your system
+ more than not using the daemon since the driver is busy with
+ maintaining the cache most of the time. Still good performance:
+ 1024, which is also the recommended value.
+
+Idle acache timeout
+MAX_CACHE_TIME
+ Time (in seconds) after which idle MDFAT/DFAT/BITFAT buffers are
+ returned to the kernel. Default is 60. (This time has only an effect
+ if the dmsdos daemon is running.)
+
+Max MDFAT cache size
+MDFATCACHESIZE
+ mdfat cache sizes -- please note that these number of fs buffers are
+ *permanently* held in memory - keep small on systems that are short
+ on memory. Default value: 40 (i.e. 20KB), which represents the MDFAT
+ space needed for approx. 5000 clusters (4000 on drivespace 3), i.e.
+ 40MB (or 160MB on 32K clusters) of compressed data.
+
+Max DFAT cache size
+DFATCACHESIZE
+ dfat cache sizes -- please note that these number of fs buffers are
+ *permanently* held in memory - keep small on systems that are short
+ on memory. Default value: 20 (i.e. 10KB), which represents the DFAT
+ space for approx. 5000 clusters, i.e. 40MB (160MB on 32K clusters)
+ of compressed data.
+
+Max BITFAT cache size
+BITFATCACHESIZE
+ bitfat cache sizes -- please note that these number of fs buffers
+ are *permanently* held in memory - keep small on systems that are
+ short on memory. Default value: 10 (i.e. 5KB), which represents the
+ BITFAT size for 400000 (200000 on stacker 4) compressed sectors,
+ i.e. 200MB (100MB on stacker 4) of compressed data.
+
+Enable write access
+DBL_WRITEACCESS
+ Say Y to compile in low-level write access code. If you say N all
+ low-level write access is replaced by an error written to syslog.
+ Say N if you want to build a read-only dmsdos module.
+
+Default loglevel
+DEFAULT_LOGLEVEL
+ Bit-coded field for debug messages (expert use only). Usually set
+ this to 0. Please read the documentation before changing this value.
+ The log level can also be changed at run time via dutil (see manpage)
+ or with a mount option.
+
+Default compression level
+DEFAULT_CF
+ Select between:
+ 0=least efficient but fastest compression
+ 11=most efficient but slowest compression
+ For slow processors you may want to use a lower value.
+ The compression level can be changed at run time with dutil (see
+ manpage) or with a mount option. If you are unsure say 11.
+
+Cluster cache size
+CCACHESIZE
+ Sets the maximum number of clusters dmsdos can keep in its cluster
+ cache. Keep small on systems with low memory - otherwise dmsdos may
+ eat up too much memory and slow down your system by causing great
+ swapping.
+ Every cluster uses 8KB (32KB on drivespace 3 and sometimes on
+ Stacker 4). You shouldn't exceed approx. 1/4 of your physical RAM,
+ so use 64 if you have only 4MB or if you have only 16MB and you are
+ using drivespace 3.
+ WARNINGS:
+ 1. If this value is too low, applications using files on a
+ compressed partition lock out each other. If it is much too low,
+ they may even lock out themselves and thus lock up the whole system.
+ Values below 64 are supposed critical, values below 32 are
+ supposed dangerous.
+ 2. You might cause a system lockup also if you use more cache than
+ you have physical RAM.
+
+Idle cluster cache timeout
+MAX_CCACHE_TIME
+ Time in seconds after which unused clusters are removed from the
+ cluster cache. Default is 240. (This time has only an effect if the
+ dmsdos daemon is running.)
+
+Maximum number of blocks to read ahead
+MAX_READA
+ Maximum number of sectors to read-ahead at once - default is 64 in
+ order to let a 32KB cluster fit in. This parameter has only an
+ effect if read-ahead is enabled in the speedup tricks & hacks section
+ below.
+
+Enable advanced read-ahead
+USE_READA_LIST
+ Use experimental (faster) read-ahead code in order to bypass
+ wait_on_buffer. If you say N the standard (slower) kernel read-ahead
+ code is used. Usually say Y unless you encounter strange problems.
+ This option only has an effect if read-ahead is turned on in the
+ speedup tricks & hacks section below.
+
+Size of read-ahead list
+READA_LIST_SIZE
+ Fine-tune advanced read-ahead code. I always set this to 256. I
+ don't know whether changing this value really speeds up something
+ - my own benchmarks didn't lead to a clear result.
+ This option has only an effect if read-ahead is turned on and
+ advanced read-ahead is enabled.
+
+Read-ahead threshold
+READA_THRESHOLD
+ Read-ahead is only done when more than this amount of bytes are read
+ at once. This prevents a badly written program from issuing thousands
+ of useless read-aheads. 4095 is a good value since this allows
+ read-ahead just for a memory page (mmap gets the data in 4096 byte
+ blocks and should have a gain from read-ahead). Lower values may
+ cause clustersize/(value+1)-1 useless read-aheads per cluster.
+ This option has only an effect if read-ahead is turned on in the
+ speedup tricks & hacks section below.
+
+Leave directories uncompressed
+SP_BIT0
+ Speedup Bit#0: avoids compression of directories. If it is set,
+ dmsdos never compresses directories for drivespace 3 and stacker
+ (others don't support compressed directories at all). Note that
+ directories are accessed very often and otherwise had to be
+ decompressed and compressed again after each access. Say Y.
+
+Leave umsdos EMD file uncompressed
+SP_BIT1
+ Speedup Bit#1 is for umsdos upon dmsdos. If set, the driver never
+ compress the --linux-.--- special file (it contains extended
+ directory information). Setting this bit is strongly recommended.
+ The special file is even more often written than a directory
+ since it contains the access times (the directory contains only
+ the modification times). You will regret compressing this file,
+ even if you own the fastest computer of the world. Say Y.
+
+Skip exact search on BITFAT allocation
+SP_BIT2
+ Speedup Bit#2: controls the free sector searching algorithm. If set,
+ dmsdos searchs faster but less carefully for free space in the CVF
+ at the cost of more fragmentation (this is *not* FAT level, but an
+ internal lower level and very awful fragmentation). If you say Y,
+ write access on large CVFs is faster (sometimes really notably
+ faster). Be warned, a CVF that is too fragmented will be set to
+ read-only, so you will have to boot Dos from time to time and run
+ the CVF maintainance tools over it. Usually say N, but saying Y
+ is not dangerous and may make you happier when writing much to a
+ CVF.
+
+Fast unmount
+SP_BIT3
+ Speedup Bit#3: speeds up CVF unmount. If set, the driver writes dirty
+ clusters that are in the cache immediately without compressing them
+ before when the filesystem is to be unmounted. This way the unmount
+ procedure will be quite fast. In contrast, if you say N the clusters
+ are compressed before. Note that compression may take some time and
+ thus blocks the system until it is ready. Since most people who
+ haven't read these lines tend to press the reset button when a
+ filesystem unmount takes somewhat longer (especially on a shutdown
+ or reboot), the default is Y. If you can tolerate the time to wait
+ and you are prepared to wait, say N.
+
+Enable write-back cluster caching (instead of write-through)
+SP_BIT4
+ Speedup Bit#4 enables write-back cluster caching. If this bit is set
+ the data in the cluster cache that have been changed by an
+ application and have to be written back to disk are not written
+ back immediately - they are kept some time in memory just in order
+ to save disk access when the application again changes the data.
+ This usually causes a significant speedup for write access,
+ especially because dmsdos also delays compression and free space
+ allocation in that case. To be honest, there's a small risk of data
+ loss if the system happens to crash before the data are written
+ back - but since your operating system is Linux a crash is rather
+ unlikely :) So say Y.
+
+Enable read-ahead
+SP_BIT5
+ Speedup Bit#5 enables cluster read-ahead in general. If you say Y,
+ this causes the driver to initiate a low-level disk access for some
+ data when it thinks they are most likely needed in a short time
+ later. This usually speeds up read access a little. The trick is
+ that the driver doesn't wait for the read-ahead disk access
+ to finish. So the disk can position its head (and so on) while the
+ system can do something else. Most likely the disk access has
+ finished when the data are actually needed - this saves some time
+ we otherwise had to wait. Well, at least this is the nice idea
+ of read-ahead. However, since read-ahead relies on a prediction,
+ there may be situations when it is useless or even a loss.
+ Just say Y.
+
+Fast BITFAT allocation
+SP_BIT6
+ Speedup Bit#6 controls the free sector searching algorithm similar
+ to Speedup Bit#2, but in a more drastic way (it simply switches it
+ off). If this bit is set, free space allocation is very fast but
+ does not care about avoiding fragmentation at all. This is not
+ recommended. BE WARNED, it can cause *much* fragmentation in very
+ short time. The "filesystem almost full" warning is suppressed.
+ This switch is meant as a last resort if you cannot tolerate
+ system slowdowns at all. Don't fill the compressed filesystem
+ up to more than 3/4 when this switch is set. Write access may
+ fail horribly and cause data loss due to too high fragmentation.
+ (Well, this is Dos/Win95 strategy - never tell the disadvantages)
+ Usually say N. If unsure about the dangers, say N also. Only say Y
+ if you really know what you are doing.
+
+Use daemon if present
+SP_BIT7
+ Speedup Bit#7 controls what jobs the dmsdos daemon has to do (if it
+ is running). If set, the daemon takes over all the compression of
+ data that have to be written to disk. This can make your system
+ respond a little faster on write access as compression will be
+ mostly done when the system is idle. This is recommended for systems
+ usually running at high processor load, or in general for slow
+ processors. In contrast, if you say N the daemon is just used for
+ dmsdos internal memory management. If unsure, say Y.
+
+Avoid fragmented writes
+SP_BIT8
+ Speedup Bit#8 controls what to do when the filesystem is too
+ fragmented for normal write access. Usually all data for one cluster
+ are stored in contiguous sectors, but if the filesystem is too
+ fragmented there may not be a 'free hole' that is large enough for
+ the data. Speedup Bit#8 controls what to do in that case. If this
+ bit is set dmsdos just returns a 'no space left on device' error and
+ refuses to write to the CVF any more.
+ Drivespace 3 and Stacker know a hack for that situation: they allow
+ storing the data of one cluster in several fragments on the disk.
+ If the bit is clear, the driver tries to write the data in several
+ fragments. Be warned, this makes future filesystem access slower as
+ it needs more disk head movements to read fragmented clusters.
+ *** Note that for Stacker this is currently not implemented ***
+ The default answer is N.
+
+Use internal daemon
+INTERNAL_DAEMON
+ Dmsdos can start a daemon that does delayed compression in the
+ background when the system is idle. It also reduces dmsdos memory
+ consumption when the driver has nothing to do for a long time.
+ The daemon exists in two variants: One is a user-level program and
+ the other one is a kernel process. The user-level program is called
+ "external daemon" while the other is the "internal daemon".
+ When the internal daemon is used, the external one won't run.
+ The internal daemon compresses faster (because the data needn't be
+ copied from kernel space to user space and back), but seems to eat
+ up much system time - use it only on *really fast* machines. Also,
+ it is started automatically by the system when it is needed (and
+ killed when it is no longer needed). On the other hand, the external
+ daemon eats up minimal system time and ressources, but must be
+ started and killed by hand.
+ *** If you have a fast processor and lots of memory (_real RAM_)
+ you probably don't need any dmsdos daemon at all.
+ Say N if unsure.
+
+Internal daemon wakeup time
+IDMSDOSD_TIME
+ Time interval in seconds for internal daemon - it awakes
+ periodically and looks for data to compress or memory to free up
+ in this interval. Set it to 30 if you are unsure. This option has
+ only an effect if the internal compression daemon is used.
+
+Disable logging completely
+NOLOG
+ Say Y if you need a really small module - this leaves out the
+ logging messages from the object code of the module. You cannot
+ use a loglevel other than 0 in this case. Usually say N.
+ Especially say N when debugging.
+
+Sequence number logging
+SEQLOG
+ Say Y to generate a sequence number for each log line. The numbers
+ are guaranteed to be uniqe and in order of call. This is for
+ debugging in order to be sure no messages have been lost (There seem
+ to be some black holes in syslog that eat messages when they occur
+ massively.)
+ Say N if you are not debugging. If you find that messages are lost,
+ change the #define LOG_BUF_LEN to at least 65536 in file
+ linux/kernel/printk.c and recompile your kernel. Please note that
+ the LOG_BUF_LEN value is expected to be a power of 2 by the Linux
+ kernel code.
+
+Writable mmap support
+DMSDOS_USE_READPAGE
+ Say Y to use the standard readpage interface (for writable mmap).
+ Say N to use old mmap interface which is read-only. Say Y except
+ you run into strange problems with writable mmaps.
+ WARNING: Old mmap interface is broken in dmsdos under some newer
+ 2.1.xx and 2.2.x kernels (it doesn't compile). It may be removed
+ from dmsdos some day.
diff --git a/src/Makefile b/src/Makefile
new file mode 100644
index 0000000..61ab1b6
--- /dev/null
+++ b/src/Makefile
@@ -0,0 +1,242 @@
+# If you want or need to change something herein, please look approx. 30 lines
+# below for the 'User Config Section'
+
+########################################################################
+# The next lines are hacks for automatic detection of different kernel setups.
+# These things must happen first, please don't change them here. They can be
+# overridden later.
+#
+# we try to guess some settings from kernel configuration :)
+-include /usr/src/linux/.config
+#
+# guess some special flags for CPU type - used for optimization only
+CPUFLAGS=
+ifeq ($(CONFIG_M386),y)
+CPUFLAGS= -m386 -DCPU=386 -DUSE_ASM
+endif
+ifeq ($(CONFIG_M486),y)
+CPUFLAGS= -m486 -DCPU=486 -DUSE_ASM
+endif
+ifeq ($(CONFIG_M586),y)
+CPUFLAGS= -m486 -DCPU=586 -DUSE_ASM
+endif
+# this one is new in 2.2.x kernels...
+ifeq ($(CONFIG_M586TSC),y)
+CPUFLAGS= -m486 -DCPU=586 -DUSE_ASM
+endif
+ifeq ($(CONFIG_M686),y)
+CPUFLAGS= -m486 -DCPU=686 -DUSE_ASM
+endif
+# this one is new in 2.3.x kernels...
+ifeq ($(CONFIG_MK7),y)
+CPUFLAGS= -m486 -DCPU=686 -DUSE_ASM
+endif
+#
+# Okay, that's it :)
+########################################################################
+
+########################################################################
+# *** User Config Section ***
+# Here you can look for settings to change if the code refuses to compile
+# out of the box. There are so many different systems. So many versions
+# of compilers. So many different bugs. :(
+#
+# Select compiler
+CC=gcc
+#
+# If cpu specific optimization fails, uncomment the next line to switch it
+# off - there are some gas versions around that don't like the cpu specific
+# asm instructions in dmsdos...
+# CPUFLAGS=
+#
+# Some gnu assembler versions have a bug in the syntax for the xlat
+# instruction. This enables a workaround. It should only be needed for very
+# old binutils versions. I don't know for which ones :(
+# CPUFLAGS+= -DGAS_XLAT_BUG
+#
+# The next lines should detect SMP configuration automatically.
+# Comment them out or set it manually if it fails.
+ifeq ($(SMP),1)
+CPUFLAGS+= -D__SMP__
+endif
+# 2.2.x kernels have this one...
+ifeq ($(CONFIG_SMP),y)
+CPUFLAGS+= -D__SMP__
+endif
+#
+# The next lines are for libc6 and enable some compatibility hacks -
+# uncomment the line with -D__FOR_LIBC6 if the code does not compile
+# *** Currently not used - code should compile under libc6 without hacks
+LIBC6FLAGS=
+#LIBC6FLAGS= -D__FOR_LIBC6
+#
+# CFLAGS for dmsdos module
+# note: some macro expansions require at least -O
+CFLAGS= -Wall -Wstrict-prototypes -O3 -fomit-frame-pointer -D__KERNEL__ -DMODULE
+CFLAGS+= $(CPUFLAGS)
+#
+# The next lines add some stuff automatically for people using modversions
+# if they fail, comment them out and set the required flags manually
+ifeq ($(CONFIG_MODVERSIONS),y)
+CFLAGS+= -DMODVERSIONS -include /usr/include/linux/modversions.h
+endif
+#
+# CFLAGS for the dmsdos daemon
+# note: some macro expansions require at least -O
+DCFLAGS= -Wall -O3 -D__DMSDOS_DAEMON__
+DCFLAGS+= $(CPUFLAGS)
+#
+# CFLAGS for the dmsdos library
+# note: some macro expansions require at least -O
+LCFLAGS= -Wall -O -ggdb -D__DMSDOS_LIB__ -DUSE_FLOCK
+LCFLAGS+= $(CPUFLAGS) $(LIBC6FLAGS)
+# uncomment the next line if you want a shared library
+# LIB_SHARED = 1
+#
+# locations where to install the module, the executables and the manpages
+# note: `uname -r` expands to the *currently running* kernel version - if it
+# is different from that in /usr/src/linux you'd better edit the next line :)
+MODULE_PREFIX=/lib/modules/`uname -r`/fs
+#MODULE_PREFIX=/lib/modules/misc
+EXEC_PREFIX=/usr/local/bin
+#
+# Okay, that's the end of the User Config Section.
+##########################################################################
+
+##########################################################################
+# The rest of this file are rules how to build which programs.
+
+all: dmsdos-config.h dmsdos.o dutil dmsdosd $(LIBDMSDOS) dcread dmsdosfsck \
+ mcdmsdos cvflist cvftest
+
+min: dmsdos-config.h dmsdos.o dutil
+
+dmsdos-config.h:
+ /bin/bash conf-wrapper.sh
+ make dep
+
+config:
+ /bin/bash Configure
+ make dep
+
+menuconfig:
+ /bin/bash Menuconfig
+ make dep
+
+dep:
+ ./check.sh
+ $(CC) -w -E -MM -D__KERNEL__ -D__MODULE__ -DMODULE dblspace*.c dstacker*.c > depend
+
+depend:
+ @touch depend
+
+DBL_OBJS = dblspace_compr.o dblspace_tables.o \
+ dblspace_alloc.o dblspace_dec.o dblspace_virtual.o \
+ dblspace_buffer.o dblspace_methsq.o \
+ dblspace_chk.o dblspace_interface.o \
+ dstacker_alloc.o dstacker_compr.o dstacker_dec.o \
+ dblspace_fileops.o dblspace_ioctl.o
+
+$(DBL_OBJS): dmsdos.h dmsdos-config.h
+
+dblspace_fileops.o : dblspace_fileops.c dblspace_fileops.c-2.0.33 \
+ dblspace_fileops.c-2.1.80 dblspace_fileops.c-2.3.10
+
+dmsdos.o: $(DBL_OBJS)
+ ld -r -o dmsdos.o $(DBL_OBJS)
+
+dutil: dutil.c dmsdos.h dmsdos-config.h
+ $(CC) $(DCFLAGS) -o dutil dutil.c
+
+DAEMON_OBJS = daemon_actions.do dblspace_compr.do dstacker_compr.do \
+ dblspace_methsq.do
+
+$(DAEMON_OBJS): dmsdos.h dmsdos-config.h
+
+%.do: %.c ; $(CC) -o $@ $(DCFLAGS) -c $<
+
+dmsdosd: $(DAEMON_OBJS)
+ $(CC) -o dmsdosd $^
+
+clean:
+ rm -f *.o *.a *.i *.s *.lo *.do *.so *.so.* dutil dmsdosd dcread \
+ dmsdosfsck cvftest cvflist mcdmsdos
+
+mrproper: clean
+ rm -f core *~ depend
+ rm -f .config* dmsdos-config.h .menuconfig*
+
+install: all
+ ./check.sh
+ install dmsdos.o -m 644 $(MODULE_PREFIX)/dmsdos.o
+ install dutil $(EXEC_PREFIX)/dutil
+ install dmsdosd $(EXEC_PREFIX)/dmsdosd
+ install dmsdosfsck $(EXEC_PREFIX)/dmsdosfsck
+ install mcdmsdos $(EXEC_PREFIX)/mcdmsdos
+ install cvflist $(EXEC_PREFIX)/cvflist
+ install cvftest $(EXEC_PREFIX)/cvftest
+
+install-min: min
+ ./check.sh
+ install dmsdos.o $(MODULE_PREFIX)/dmsdos.o
+ install dutil $(EXEC_PREFIX)/dutil
+
+uninstall:
+ rm -f $(MODULE_PREFIX)/dmsdos.o
+ rm -f $(EXEC_PREFIX)/dutil
+ rm -f $(EXEC_PREFIX)/dmsdosd
+ rm -f $(EXEC_PREFIX)/dmsdosfsck
+ rm -f $(EXEC_PREFIX)/mcdmsdos
+ rm -f $(EXEC_PREFIX)/cvflist
+ rm -f $(EXEC_PREFIX)/cvftest
+
+messages:
+ grep DMSDOS ../doc/messages.doc | sort -d -b -f > /tmp/messdoc
+ ./listmsg.sh -LOG > /tmp/messsrc
+ diff -d -U 0 -w /tmp/messdoc /tmp/messsrc | grep DMSDOS
+
+LIB_OBJS = lib_interface.lo dblspace_interface.lo dblspace_dec.lo \
+ dblspace_compr.lo dblspace_methsq.lo dblspace_alloc.lo \
+ dblspace_chk.lo dblspace_tables.lo dstacker_compr.lo \
+ dstacker_dec.lo dstacker_alloc.lo
+
+$(LIB_OBJS): dmsdos.h dmsdos-config.h lib_interface.h
+
+ifndef LIB_SHARED
+
+LIBDMSDOS = libdmsdos.a
+
+%.lo: %.c ; $(CC) -o $@ $(LCFLAGS) -c $<
+
+$(LIBDMSDOS): $(LIB_OBJS)
+ ar rcs $(LIBDMSDOS) $^
+ ranlib $(LIBDMSDOS)
+
+else
+
+%.lo: %.c ; $(CC) --shared -o $@ $(LCFLAGS) -c $<
+
+LIBDMSDOS = libdmsdos.so.0.9.2
+
+$(LIBDMSDOS): $(LIB_OBJS)
+ ld --shared --soname=$(LIBDMSDOS) -o $(LIBDMSDOS) $^
+ ln -s $(LIBDMSDOS) libdmsdos.so
+
+endif
+
+dcread: dcread.c $(LIBDMSDOS) dmsdos.h dmsdos-config.h
+ $(CC) -Wall -ggdb -o dcread dcread.c -L. -ldmsdos
+
+mcdmsdos: mcdmsdos.c $(LIBDMSDOS) dmsdos.h dmsdos-config.h
+ $(CC) -Wall -ggdb -o mcdmsdos mcdmsdos.c -L. -ldmsdos
+
+dmsdosfsck: dmsdosfsck.c $(LIBDMSDOS) dmsdos.h dmsdos-config.h
+ $(CC) -Wall -o dmsdosfsck dmsdosfsck.c -L. -ldmsdos
+
+cvftest: cvftest.c
+ $(CC) -Wall -o cvftest cvftest.c
+
+cvflist: cvflist.c
+ $(CC) -Wall -o cvflist cvflist.c
+
+-include depend
diff --git a/src/Makefile.cygwinb20 b/src/Makefile.cygwinb20
new file mode 100644
index 0000000..9ef003a
--- /dev/null
+++ b/src/Makefile.cygwinb20
@@ -0,0 +1,103 @@
+# Makefile for cygwin beta 20 compiler
+#
+# If you want or need to change something herein, please look
+# below for the 'User Config Section'
+
+
+########################################################################
+# *** User Config Section ***
+# Here you can look for settings to change if the code refuses to compile
+# out of the box. There are so many different systems. So many versions
+# of compilers. So many different bugs. :(
+#
+# Select compiler
+CC=gcc
+#
+# If cpu specific optimization fails, uncomment the empty CPUFLAGS line to
+# switch it off - there are some gas versions around that don't like the
+# cpu specific asm instructions in dmsdos...
+# There's no way to detect this automatically in _all_ Win32 OS versions
+CPUFLAGS= -DCPU=486 -m486 -DUSE_ASM
+#CPUFLAGS=
+#
+# Some gnu assembler versions have a bug in the syntax for the xlat
+# instruction. This enables a workaround. It should only be needed for very
+# old binutils versions. I don't know for which ones :(
+# CPUFLAGS+= -DGAS_XLAT_BUG
+#
+# CFLAGS for the dmsdos library
+# note: some macro expansions require at least -O
+# cygwin seems to have neither flock nor sopen support (Uuhhh....)
+# well it's bad to compile code without file locking but we have no choice
+#LCFLAGS= -Wall -O -ggdb -D__DMSDOS_LIB__ -DUSE_FLOCK
+LCFLAGS= -Wall -O -ggdb -D__DMSDOS_LIB__
+LCFLAGS+= $(CPUFLAGS) $(LIBC6FLAGS)
+# uncomment the next line if you want a shared library
+# does not work yet under win32
+# LIB_SHARED = 1
+#
+#
+# Okay, that's the end of the User Config Section.
+##########################################################################
+
+##########################################################################
+# The rest of this file are rules how to build which programs.
+
+all: dmsdos-config.h $(LIBDMSDOS) dcread dmsdosfsck mcdmsdos
+
+dmsdos-config.h:
+ bash Configure
+
+config:
+ bash Configure
+
+clean:
+ rm -f *.o *.a *.i *.s *.lo *.do *.so *.so.* dutil dmsdosd dcread \
+ dmsdosfsck cvftest cvflist mcdmsdos
+
+dep:
+
+mrproper: clean
+ rm -f core *~ depend
+ rm -f .config* dmsdos-config.h .menuconfig*
+
+
+LIB_OBJS = lib_interface.lo dblspace_interface.lo dblspace_dec.lo \
+ dblspace_compr.lo dblspace_methsq.lo dblspace_alloc.lo \
+ dblspace_chk.lo dblspace_tables.lo dstacker_compr.lo \
+ dstacker_dec.lo dstacker_alloc.lo
+
+$(LIB_OBJS): dmsdos.h dmsdos-config.h lib_interface.h
+
+ifndef LIB_SHARED
+
+LIBDMSDOS = libdmsdos.a
+
+%.lo: %.c ; $(CC) -o $@ $(LCFLAGS) -c $<
+
+$(LIBDMSDOS): $(LIB_OBJS)
+ ar rcs $(LIBDMSDOS) $^
+ ranlib $(LIBDMSDOS)
+
+else
+
+%.lo: %.c ; $(CC) --shared -o $@ $(LCFLAGS) -c $<
+
+LIBDMSDOS = libdmsdos.so.0.9.2
+
+$(LIBDMSDOS): $(LIB_OBJS)
+ ld --shared --soname=$(LIBDMSDOS) -o $(LIBDMSDOS) $^
+ ln -s $(LIBDMSDOS) libdmsdos.so
+
+endif
+
+dcread: dcread.c $(LIBDMSDOS) dmsdos.h dmsdos-config.h
+ $(CC) -Wall -ggdb -o dcread dcread.c -L. -ldmsdos
+
+mcdmsdos: mcdmsdos.c $(LIBDMSDOS) dmsdos.h dmsdos-config.h
+ $(CC) -Wall -ggdb -o mcdmsdos mcdmsdos.c -L. -ldmsdos
+
+dmsdosfsck: dmsdosfsck.c $(LIBDMSDOS) dmsdos.h dmsdos-config.h
+ $(CC) -Wall -o dmsdosfsck dmsdosfsck.c -L. -ldmsdos
+
+
diff --git a/src/Makefile.kernel b/src/Makefile.kernel
new file mode 100644
index 0000000..0134f1a
--- /dev/null
+++ b/src/Makefile.kernel
@@ -0,0 +1,54 @@
+#
+# Makefile for dmsdos support.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definitions are now in the main makefile...
+
+ifeq ($(CONFIG_M386),y)
+CPUFLAGS= -DUSE_ASM
+endif
+ifeq ($(CONFIG_M486),y)
+CPUFLAGS= -DUSE_ASM
+endif
+ifeq ($(CONFIG_M586),y)
+CPUFLAGS= -DUSE_ASM
+endif
+ifeq ($(CONFIG_M686),y)
+CPUFLAGS= -DUSE_ASM
+endif
+
+########################################################################
+# *** User Config Section ***
+# Here you can look for settings to change if the code refuses to compile
+# out of the box. There are so many different systems. So many versions
+# of compilers. So many different bugs. :(
+#
+# If cpu specific optimization fails, uncomment the next line to switch it
+# off - there are some gas versions around that don't like the cpu specific
+# asm instructions in dmsdos...
+# CPUFLAGS=
+#
+# Some gnu assembler versions have a bug in the syntax for the xlat
+# instruction. This enables a workaround. It should only be needed for very
+# old binutils versions. I don't know for which ones :(
+# CPUFLAGS+= -DGAS_XLAT_BUG
+#
+# Okay, that's the end of the User Config Section.
+##########################################################################
+
+CFLAGS+= $(CPUFLAGS)
+
+O_TARGET := dmsdos.o
+O_OBJS := dblspace_compr.o dblspace_tables.o \
+ dblspace_alloc.o dblspace_dec.o dblspace_virtual.o \
+ dblspace_buffer.o dblspace_methsq.o \
+ dblspace_chk.o dblspace_interface.o \
+ dstacker_alloc.o dstacker_compr.o dstacker_dec.o \
+ dblspace_fileops.o dblspace_ioctl.o
+OX_OBJS :=
+M_OBJS := $(O_TARGET)
+
+include $(TOPDIR)/Rules.make
diff --git a/src/Makefile.module b/src/Makefile.module
new file mode 100644
index 0000000..5ebb5ef
--- /dev/null
+++ b/src/Makefile.module
@@ -0,0 +1,28 @@
+# Where to look for kernel
+KERNEL_LOCATION=/usr/src/linux
+# Target object file if any
+O_TARGET := dmsdos.o
+# Regular object files
+O_OBJS = dblspace_compr.o dblspace_tables.o \
+ dblspace_alloc.o dblspace_dec.o dblspace_virtual.o \
+ dblspace_buffer.o dblspace_methsq.o \
+ dblspace_chk.o dblspace_interface.o \
+ dstacker_alloc.o dstacker_compr.o dstacker_dec.o \
+ dblspace_fileops.o dblspace_ioctl.o
+# Objects with exported symbols (-DEXPORT_SYMTAB)
+OX_OBJS =
+# Module objects
+M_OBJS = $(O_TARGET)
+# Module only objects with exported symbols (-DEXPORT_SYMTAB)
+MX_OBJS =
+# Kernel only objects
+L_OBJS =
+# Kernel only objects with exported symbols (-DEXPORT_SYMTAB)
+LX_OBJS =
+# Additional CFLAGS
+EXTRA_CFLAGS = -DUSE_ASM
+
+my_modules:
+ DIR=`pwd`; (cd $(KERNEL_LOCATION); make SUBDIRS=$$DIR modules)
+
+include $(KERNEL_LOCATION)/Rules.make
diff --git a/src/Makefile.old b/src/Makefile.old
new file mode 100644
index 0000000..69eb38c
--- /dev/null
+++ b/src/Makefile.old
@@ -0,0 +1,215 @@
+
+# If you want or need to change something herein, please look approx. 30 lines
+# below for the 'User Config Section'
+
+########################################################################
+# The next lines are hacks for automatic detection of different kernel setups.
+# These things must happen first, please don't change them here. They can be
+# overridden later.
+#
+# we try to guess some settings from kernel configuration :)
+-include /usr/src/linux/.config
+#
+# guess some special flags for CPU type - used for optimization only
+CPUFLAGS=
+ifeq ($(CONFIG_M386),y)
+CPUFLAGS= -m386 -DCPU=386 -DUSE_ASM
+endif
+ifeq ($(CONFIG_M486),y)
+CPUFLAGS= -m486 -DCPU=486 -DUSE_ASM
+endif
+ifeq ($(CONFIG_M586),y)
+CPUFLAGS= -m486 -DCPU=586 -DUSE_ASM
+endif
+ifeq ($(CONFIG_M686),y)
+CPUFLAGS= -m486 -DCPU=686 -DUSE_ASM
+endif
+#
+# Okay, that's it :)
+########################################################################
+
+########################################################################
+# *** User Config Section ***
+# Here you can look for settings to change if the code refuses to compile
+# out of the box. There are so many different systems. So many versions
+# of compilers. So many different bugs. :(
+#
+# Select compiler
+CC=gcc
+#
+# If cpu specific optimization fails, uncomment the next line to switch it
+# off - there are some gas versions around that don't like the cpu specific
+# asm instructions in dmsdos...
+# CPUFLAGS=
+#
+# Some gnu assembler versions have a bug in the syntax for the xlat
+# instruction. This enables a workaround. It should only be needed for very
+# old binutils versions. I don't know for which ones :(
+# CPUFLAGS+= -DGAS_XLAT_BUG
+#
+# The next lines should detect SMP configuration automatically.
+# Comment them out or set it manually if it fails.
+ifeq ($(SMP),1)
+CPUFLAGS+= -D__SMP__
+endif
+#
+# The next lines are for libc6 and enable some compatibility hacks -
+# uncomment the line with -D__FOR_LIBC6 if the code does not compile
+# *** Currently not used - code should compile under libc6 without hacks
+LIBC6FLAGS=
+#LIBC6FLAGS= -D__FOR_LIBC6
+#
+# CFLAGS for dmsdos module
+# note: some macro expansions require at least -O
+CFLAGS= -Wall -Wstrict-prototypes -O3 -fomit-frame-pointer -D__KERNEL__ -DMODULE
+CFLAGS+= $(CPUFLAGS)
+#
+# The next lines add some stuff automatically for people using modversions
+# if they fail, comment them out and set the required flags manually
+ifeq ($(CONFIG_MODVERSIONS),y)
+CFLAGS+= -DMODVERSIONS -include /usr/include/linux/modversions.h
+endif
+#
+# CFLAGS for the dmsdos daemon
+# note: some macro expansions require at least -O
+DCFLAGS= -Wall -O3 -D__DMSDOS_DAEMON__
+DCFLAGS+= $(CPUFLAGS)
+#
+# CFLAGS for the dmsdos library
+# note: some macro expansions require at least -O
+LCFLAGS= -Wall -O -ggdb -D__DMSDOS_LIB__
+LCFLAGS+= $(CPUFLAGS) $(LIBC6FLAGS)
+#
+# locations where to install the module, the executables and the manpages
+# note: `uname -r` expands to the *currently running* kernel version - if it
+# is different from that in /usr/src/linux you'd better edit the next line :)
+MODULE_PREFIX=/lib/modules/`uname -r`/fs
+#MODULE_PREFIX=/lib/modules/misc
+EXEC_PREFIX=/usr/local/bin
+#
+# Okay, that's the end of the User Config Section.
+##########################################################################
+
+##########################################################################
+# The rest of this file are rules how to build which programs.
+
+all: dmsdos-config.h dmsdos.o dutil dmsdosd libdmsdos.a dcread dmsdosfsck \
+ mcdmsdos cvflist cvftest
+
+min: dmsdos-config.h dmsdos.o dutil
+
+dmsdos-config.h:
+ /bin/bash conf-wrapper.sh
+ make dep
+
+config:
+ /bin/bash Configure
+ make dep
+
+menuconfig:
+ /bin/bash Menuconfig
+ make dep
+
+dep:
+ ./check.sh
+ $(CC) -w -E -MM -D__KERNEL__ -D__MODULE__ -DMODULE dblspace*.c dstacker*.c > depend
+
+depend:
+ @touch depend
+
+DBL_OBJS = dblspace_compr.o dblspace_tables.o \
+ dblspace_alloc.o dblspace_dec.o dblspace_virtual.o \
+ dblspace_buffer.o dblspace_methsq.o \
+ dblspace_chk.o dblspace_interface.o \
+ dstacker_alloc.o dstacker_compr.o dstacker_dec.o \
+ dblspace_fileops.o dblspace_ioctl.o
+
+$(DBL_OBJS): dmsdos.h dmsdos-config.h
+
+dmsdos.o: $(DBL_OBJS)
+ ld -r -o dmsdos.o $(DBL_OBJS)
+
+dutil: dutil.c dmsdos.h dmsdos-config.h
+ $(CC) $(DCFLAGS) -o dutil dutil.c
+
+DAEMON_OBJS = daemon_actions.do dblspace_compr.do dstacker_compr.do \
+ dblspace_methsq.do
+
+$(DAEMON_OBJS): dmsdos.h dmsdos-config.h
+
+%.do: %.c ; $(CC) -o $@ $(DCFLAGS) -c $<
+
+dmsdosd: $(DAEMON_OBJS)
+ $(CC) -o dmsdosd $^
+
+clean:
+ rm -f *.o *.a *.i *.s *.lo *.do *.so dutil dmsdosd dcread \
+ dmsdosfsck cvftest cvflist mcdmsdos
+
+mrproper: clean
+ rm -f core *~ depend
+ rm -f .config* dmsdos-config.h .menuconfig*
+
+install: all
+ ./check.sh
+ install dmsdos.o -m 644 $(MODULE_PREFIX)/dmsdos.o
+ install dutil $(EXEC_PREFIX)/dutil
+ install dmsdosd $(EXEC_PREFIX)/dmsdosd
+ install dmsdosfsck $(EXEC_PREFIX)/dmsdosfsck
+ install mcdmsdos $(EXEC_PREFIX)/mcdmsdos
+ install cvflist $(EXEC_PREFIX)/cvflist
+ install cvftest $(EXEC_PREFIX)/cvftest
+
+install-min: min
+ ./check.sh
+ install dmsdos.o $(MODULE_PREFIX)/dmsdos.o
+ install dutil $(EXEC_PREFIX)/dutil
+
+uninstall:
+ rm -f $(MODULE_PREFIX)/dmsdos.o
+ rm -f $(EXEC_PREFIX)/dutil
+ rm -f $(EXEC_PREFIX)/dmsdosd
+ rm -f $(EXEC_PREFIX)/dmsdosfsck
+ rm -f $(EXEC_PREFIX)/mcdmsdos
+ rm -f $(EXEC_PREFIX)/cvflist
+ rm -f $(EXEC_PREFIX)/cvftest
+
+messages:
+ grep DMSDOS ../doc/messages.doc | sort -d -b -f > /tmp/messdoc
+ ./listmsg.sh -LOG > /tmp/messsrc
+ diff -d -U 0 -w /tmp/messdoc /tmp/messsrc | grep DMSDOS
+
+LIB_OBJS = lib_interface.lo dblspace_interface.lo dblspace_dec.lo \
+ dblspace_compr.lo dblspace_methsq.lo dblspace_alloc.lo \
+ dblspace_chk.lo dblspace_tables.lo dstacker_compr.lo \
+ dstacker_dec.lo dstacker_alloc.lo
+
+$(LIB_OBJS): dmsdos.h dmsdos-config.h lib_interface.h
+
+%.lo: %.c ; $(CC) -o $@ $(LCFLAGS) -c $<
+
+dmsdos_library.lo: $(LIB_OBJS)
+ ld -r -o dmsdos_library.lo $^
+
+libdmsdos.a: dmsdos_library.lo
+ ar rcs libdmsdos.a dmsdos_library.lo
+
+libdmsdos.so: dmsdos_library.lo
+ ld -shared -o libdmsdos.so dmsdos_library.lo
+
+dcread: dcread.c libdmsdos.a dmsdos.h dmsdos-config.h
+ $(CC) -Wall -ggdb -o dcread dcread.c -L. -ldmsdos
+
+mcdmsdos: mcdmsdos.c libdmsdos.a dmsdos.h dmsdos-config.h
+ $(CC) -Wall -ggdb -o mcdmsdos mcdmsdos.c -L. -ldmsdos
+
+dmsdosfsck: dmsdosfsck.c libdmsdos.a dmsdos.h dmsdos-config.h
+ $(CC) -Wall -o dmsdosfsck dmsdosfsck.c -L. -ldmsdos
+
+cvftest: cvftest.c
+ $(CC) -Wall -o cvftest cvftest.c
+
+cvflist: cvflist.c
+ $(CC) -Wall -o cvflist cvflist.c
+
+-include depend
diff --git a/src/Menuconfig b/src/Menuconfig
new file mode 100644
index 0000000..196b4fa
--- /dev/null
+++ b/src/Menuconfig
@@ -0,0 +1,1187 @@
+#! /bin/sh
+#
+# This script is used to configure dmsdos options.
+# It is shamelessly stolen from the Linux kernel.
+# For a list of credits see there.
+#
+#----------------------------------------------------------------------------
+
+#
+# Make sure we're really running bash.
+#
+[ -z "$BASH" ] && { echo "Menuconfig requires bash" 1>&2; exit 1; }
+
+#
+# Cache function definitions, turn off posix compliance
+#
+set -h +o posix
+
+
+#
+# If you prefer all kernel options listed in a single menu rather than
+# the standard menu hierarchy, set SINGLE_MENU_MODE to "TRUE" in your
+# environment.
+#
+single_menu_mode="${SINGLE_MENU_MODE:-FALSE}"
+
+
+#
+# Load the functions used by the config.in files.
+#
+# I do this because these functions must be redefined depending
+# on whether they are being called for interactive use or for
+# saving a configuration to a file.
+#
+# Thank the heavens bash supports nesting function definitions.
+#
+load_functions () {
+
+#
+# Additional comments
+#
+function comment () {
+ comment_ctr=$[ comment_ctr + 1 ]
+ echo -ne "': $comment_ctr' '--- $1' " >>MCmenu
+}
+
+#
+# Don't need this yet, but we don't want to puke either.
+#
+function define_bool () {
+ :
+}
+
+#
+# Create a boolean (Yes/No) function for our current menu
+# which calls our local bool function.
+#
+function bool () {
+ eval $2=\${$2:-'n'} x=\$$2
+
+ case $x in
+ y|m) flag="*" ;;
+ n) flag=" " ;;
+ esac
+
+ echo -ne "'$2' '[$flag] $1' " >>MCmenu
+
+ echo -e "function $2 () { l_bool '$2' \"\$1\" ;}\n" >>MCradiolists
+}
+
+#
+# Create a tristate (Yes/No/Module) radiolist function
+# which calls our local tristate function.
+#
+# Collapses to a boolean (Yes/No) if module support is disabled.
+#
+function tristate () {
+ if [ "$CONFIG_MODULES" != "y" ]
+ then
+ bool "$1" "$2"
+ else
+ eval $2=\${$2:-'n'} x=\$$2
+
+ case $x in
+ y) flag="*" ;;
+ m) flag="M" ;;
+ *) flag=" " ;;
+ esac
+
+ echo -ne "'$2' '<$flag> $1' " >>MCmenu
+
+ echo -e "
+ function $2 () { l_tristate '$2' \"\$1\" ;}" >>MCradiolists
+ fi
+}
+
+#
+# Create a tristate radiolist function which is dependent on
+# another kernel configuration option.
+#
+# Quote from the original configure script:
+#
+# If the option we depend upon is a module,
+# then the only allowable options are M or N. If Y, then
+# this is a normal tristate. This is used in cases where modules
+# are nested, and one module requires the presence of something
+# else in the kernel.
+#
+function dep_tristate () {
+ if [ "$CONFIG_MODULES" != "y" ]
+ then
+ bool "$1" "$2"
+ else
+ if eval [ "_$3" != "_m" ]
+ then
+ tristate "$1" "$2" $3
+ else
+ mod_bool "$1" "$2"
+ fi
+ fi
+}
+
+#
+# Add a menu item which will call our local int function.
+#
+function int () {
+ eval $2=\${$2:-"$3"} x=\$$2
+
+ echo -ne "'$2' '($x) $1' " >>MCmenu
+
+ echo -e "function $2 () { l_int '$1' '$2' '$3' '$x' ;}" >>MCradiolists
+}
+
+#
+# Add a menu item which will call our local int function.
+#
+function hex () {
+ eval $2=\${$2:-"$3"} x=\${$2##*[x,X]}
+
+ echo -ne "'$2' '($x) $1' " >>MCmenu
+
+ echo -e "function $2 () { l_hex '$1' '$2' '$3' '$x' ;}" >>MCradiolists
+}
+
+#
+# Add a menu item which will call our local One-of-Many choice list.
+#
+function choice () {
+ #
+ # Need to remember params cause they're gonna get reset.
+ #
+ title=$1
+ choices=$2
+ default=$3
+ current=
+
+ #
+ # Find out if one of the choices is already set.
+ # If it's not then make it the default.
+ #
+ set -- $choices
+ firstchoice=$2
+
+ while [ -n "$2" ]
+ do
+ if eval [ "_\$$2" = "_y" ]
+ then
+ current=$1
+ break
+ fi
+ shift ; shift
+ done
+
+ : ${current:=$default}
+
+ echo -ne "'$firstchoice' '($current) $title' " >>MCmenu
+
+ echo -e "
+ function $firstchoice () \
+ { l_choice '$title' \"$choices\" $current ;}" >>MCradiolists
+}
+
+} # END load_functions()
+
+
+
+
+
+#
+# Extract available help for an option from Configure.help
+# and send it to standard output.
+#
+# Most of this function was borrowed from the original kernel
+# Configure script.
+#
+function extract_help () {
+ if [ -f Configure.help ]
+ then
+ #first escape regexp special characters in the argument:
+ var=$(echo "$1"|sed 's/[][\/.^$*]/\\&/g')
+ #now pick out the right help text:
+ text=$(sed -n "/^$var[ ]*\$/,\${
+ /^$var[ ]*\$/d
+ /^#.*/d
+ /^[ ]*\$/q
+ s/^ //
+ p
+ }" Configure.help)
+
+ if [ -z "$text" ]
+ then
+ echo "There is no help available for this dmsdos option."
+ return 1
+ else
+ echo "$text"
+ fi
+ else
+ echo "There is no help available for this dmsdos option."
+ return 1
+ fi
+}
+
+#
+# Activate a help dialog.
+#
+function help () {
+ if extract_help $1 >help.out
+ then
+ $DIALOG --backtitle "$backtitle" --title "$2"\
+ --textbox help.out $ROWS $COLS
+ else
+ $DIALOG --backtitle "$backtitle" \
+ --textbox help.out $ROWS $COLS
+ fi
+ rm help.out
+}
+
+#
+# Show the README file.
+#
+function show_readme () {
+ $DIALOG --backtitle "$backtitle" \
+ --textbox scripts/README.Menuconfig $ROWS $COLS
+}
+
+#
+# Begin building the dialog menu command and Initialize the
+# Radiolist function file.
+#
+function menu_name () {
+ echo -ne "$DIALOG --title '$1'\
+ --backtitle '$backtitle' \
+ --menu '$menu_instructions' \
+ $ROWS $COLS $((ROWS-10)) \
+ '$default' " >MCmenu
+ >MCradiolists
+}
+
+#
+# Add a submenu option to the menu currently under construction.
+#
+function submenu () {
+ echo -ne "'activate_menu $2' '$1 --->' " >>MCmenu
+}
+
+#
+# Create a menu entry to handle the traditional sound configuration.
+#
+function soundcfg () {
+ echo -ne "'l_soundcfg' "\
+ "'Old configuration script "\
+ "(For: SM Wave, PSS & AudioTrix Pro) -->' " >>MCmenu
+}
+
+#
+# Startup the traditional sound configuration program.
+#
+function l_soundcfg () {
+ clear
+ $MAKE -C drivers/sound config
+}
+
+#
+# Handle a boolean (Yes/No) option.
+#
+function l_bool () {
+ if [ -n "$2" ]
+ then
+ case "$2" in
+ y|m) eval $1=y ;;
+ c) eval x=\$$1
+ case $x in
+ y) eval $1=n ;;
+ n) eval $1=y ;;
+ esac ;;
+ *) eval $1=n ;;
+ esac
+ else
+ echo -ne "\007"
+ fi
+}
+
+#
+# Same as bool() except options are (Module/No)
+#
+function mod_bool () {
+ eval $2=\${$2:-'n'} x=\$$2
+
+ case $x in
+ y|m) flag='M' ;;
+ *) flag=' ' ;;
+ esac
+
+ echo -ne "'$2' '<$flag> $1' " >>MCmenu
+
+ echo -e "function $2 () { l_mod_bool '$2' \"\$1\" ;}" >>MCradiolists
+}
+
+#
+# Same as l_bool() except options are (Module/No)
+#
+function l_mod_bool() {
+ if [ -n "$2" ]
+ then
+ case "$2" in
+ y) echo -en "\007"
+ ${DIALOG} --backtitle "$backtitle" \
+ --infobox "\
+This feature depends on another which has been configured as a module. \
+As a result, this feature will be built as a module." 4 70
+ sleep 5
+ eval $1=m ;;
+ m) eval $1=m ;;
+ c) eval x=\$$1
+ case $x in
+ m) eval $1=n ;;
+ n) eval $1=m ;;
+ esac ;;
+ *) eval $1=n ;;
+ esac
+ else
+ echo -ne "\007"
+ fi
+}
+
+#
+# Handle a tristate (Yes/No/Module) option.
+#
+function l_tristate () {
+ if [ -n "$2" ]
+ then
+ eval x=\$$1
+
+ case "$2" in
+ y) eval $1=y ;;
+ m) eval $1=m ;;
+ c) eval x=\$$1
+ case $x in
+ y) eval $1=n ;;
+ n) eval $1=m ;;
+ m) eval $1=y ;;
+ esac ;;
+ *) eval $1=n ;;
+ esac
+ else
+ echo -ne "\007"
+ fi
+}
+
+#
+# Create a dialog for entering an integer into a kernel option.
+#
+function l_int () {
+ while true
+ do
+ if $DIALOG --title "$1" \
+ --backtitle "$backtitle" \
+ --inputbox "$inputbox_instructions_int" \
+ 10 75 "$4" 2>MCdialog.out
+ then
+ answer="`cat MCdialog.out`"
+ answer="${answer:-$3}"
+
+ # Avoid problems with GNU vs POSIX expr semantics.
+ if expr "$answer" : '0$\|-[1-9][0-9]*$\|[1-9][0-9]*$' >/dev/null
+ then
+ eval $2="$answer"
+ else
+ eval $2="$3"
+ echo -en "\007"
+ ${DIALOG} --backtitle "$backtitle" \
+ --infobox "You have made an invalid entry." 3 43
+ sleep 2
+ fi
+
+ break
+ fi
+
+ help "$2" "$1"
+ done
+}
+
+#
+# Create a dialog for entering a hexadecimal into a kernel option.
+#
+function l_hex () {
+ while true
+ do
+ if $DIALOG --title "$1" \
+ --backtitle "$backtitle" \
+ --inputbox "$inputbox_instructions_hex" \
+ 10 75 "$4" 2>MCdialog.out
+ then
+ answer="`cat MCdialog.out`"
+ answer="${answer:-$3}"
+ answer="${answer##*[x,X]}"
+
+ # Avoid problems with GNU vs POSIX expr semantics.
+ if expr "$answer" : '[0-9a-fA-F][0-9a-fA-F]*$' >/dev/null
+ then
+ eval $2="$answer"
+ else
+ eval $2="$3"
+ echo -en "\007"
+ ${DIALOG} --backtitle "$backtitle" \
+ --infobox "You have made an invalid entry." 3 43
+ sleep 2
+ fi
+
+ break
+ fi
+
+ help "$2" "$1"
+ done
+}
+
+#
+# Handle a one-of-many choice list.
+#
+function l_choice () {
+ #
+ # Need to remember params cause they're gonna get reset.
+ #
+ title="$1"
+ choices="$2"
+ current="$3"
+
+ #
+ # Scan current value of choices and set radiolist switches.
+ #
+ list=
+ set -- $choices
+ firstchoice=$2
+ while [ -n "$2" ]
+ do
+ case "$1" in
+ "$current") list="$list $2 $1 ON " ;;
+ *) list="$list $2 $1 OFF " ;;
+ esac
+
+ shift ; shift
+ done
+
+ while true
+ do
+ if $DIALOG --title "$title" \
+ --backtitle "$backtitle" \
+ --radiolist "$radiolist_instructions" \
+ 15 70 6 $list 2>MCdialog.out
+ then
+ choice=`cat MCdialog.out`
+ break
+ fi
+
+ help "$firstchoice" "$title"
+ done
+
+ #
+ # Now set the boolean value of each option based on
+ # the selection made from the radiolist.
+ #
+ set -- $choices
+ while [ -n "$2" ]
+ do
+ if [ "$2" = "$choice" ]
+ then
+ eval $2="y"
+ else
+ eval $2="n"
+ fi
+
+ shift ; shift
+ done
+}
+
+
+#
+# A faster awk based recursive parser. (I hope)
+#
+function parser1 () {
+awk '
+BEGIN {
+ menu_no = 0
+ comment_is_option = 0
+ parser("'$CONFIG_IN'","MCmenu0")
+}
+
+function parser(ifile,menu) {
+
+ while (getline <ifile) {
+ if ($1 == "mainmenu_option") {
+ comment_is_option = "1"
+ }
+ else if ($1 == "comment" && comment_is_option == "1") {
+ comment_is_option= "0"
+ sub($1,"",$0)
+ ++menu_no
+
+ printf("submenu %s MCmenu%s\n", $0, menu_no) >>menu
+
+ printf( "function MCmenu%s () {\n"\
+ "default=$1\n"\
+ "menu_name %s\n",\
+ menu_no, $0) >"MCmenu"menu_no
+
+ parser(ifile, "MCmenu"menu_no)
+ }
+ else if ($1 ~ "endmenu") {
+ printf("}\n") >>menu
+ return
+ }
+ else if ($0 ~ /^#|\$MAKE|mainmenu_name/) {
+ printf("") >>menu
+ }
+ else if ($1 == "source") {
+ # Yuk! Blah! Phooey!
+ if ($2 ~ "drivers/sound") {
+ printf("soundcfg\n") >>menu
+ }
+
+ parser($2,menu)
+ }
+ else {
+ print >>menu
+ }
+ }
+}'
+}
+
+#
+# Secondary parser for single menu mode.
+#
+function parser2 () {
+awk '
+BEGIN {
+ parser("'$CONFIG_IN'","MCmenu0")
+}
+
+function parser(ifile,menu) {
+
+ while (getline <ifile) {
+ if ($1 ~ /mainmenu_option|endmenu/) {
+ printf("") >>menu
+ }
+ else if ($0 ~ /^#|\$MAKE|mainmenu_name/) {
+ printf("") >>menu
+ }
+ else if ($1 == "source") {
+ if ($2 ~ "drivers/sound") {
+ printf("soundcfg\n") >>menu
+ }
+ parser($2,menu)
+ }
+ else {
+ print >>menu
+ }
+ }
+}'
+}
+
+#
+# Parse all the config.in files into mini scripts.
+#
+function parse_config_files () {
+ rm -f MCmenu*
+
+ echo "function MCmenu0 () {" >MCmenu0
+ echo 'default=$1' >>MCmenu0
+ echo "menu_name 'Main Menu'" >>MCmenu0
+
+ if [ "_$single_menu_mode" = "_TRUE" ]
+ then
+ parser2
+ else
+ parser1
+ fi
+
+ echo "comment ''" >>MCmenu0
+ echo "g_alt_config" >>MCmenu0
+ echo "s_alt_config" >>MCmenu0
+
+ echo "}" >>MCmenu0
+
+ #
+ # These mini scripts must be sourced into the current
+ # environment in order for all of this to work. Leaving
+ # them on the disk as executables screws up the recursion
+ # in activate_menu(), among other things. Once they are
+ # sourced we can discard them.
+ #
+ for i in MCmenu*
+ do
+ source ./$i
+ done
+
+ rm -f MCmenu*
+}
+
+#
+# This is the menu tree's bootstrap.
+#
+# Executes the parsed menus on demand and creates a set of functions,
+# one per configuration option. These functions will in turn execute
+# dialog commands or recursively call other menus.
+#
+function activate_menu () {
+ while true
+ do
+ comment_ctr=0 #So comment lines get unique tags
+
+ $1 "$default" #Create the lxdialog menu & functions
+
+ if [ "$?" != "0" ]
+ then
+ clear
+ cat <<EOM
+
+Menuconfig has encountered a possible error in one of the kernel's
+configuration files and is unable to continue.
+
+Please report this to the author <roadcapw@titus.org>. You may also
+send a problem report to linux-kernel@vger.rutgers.edu or post a
+message to the linux.dev.kernel news group.
+
+Please indicate the kernel version you are trying to configure and
+which menu you were trying to enter when this error occurred.
+
+EOM
+ cleanup
+ exit 1
+ fi
+
+ . ./MCradiolists #Source the menu's functions
+
+ . ./MCmenu 2>MCdialog.out #Activate the lxdialog menu
+ ret=$?
+
+ read selection <MCdialog.out
+
+ case "$ret" in
+ 0|3|4|5|6)
+ defaults="$selection$defaults" #pseudo stack
+ case "$ret" in
+ 0) eval $selection ;;
+ 3) eval $selection y ;;
+ 4) eval $selection n ;;
+ 5) eval $selection m ;;
+ 6) eval $selection c ;;
+ esac
+ default="${defaults%%*}" defaults="${defaults#*}"
+ ;;
+ 2)
+ default="${selection%%\ *}"
+
+ case "$selection" in
+ *"-->"*|*"alt_config"*)
+ show_readme ;;
+ *)
+ eval help $selection ;;
+ esac
+ ;;
+ 255|1)
+ break
+ ;;
+ 139)
+ stty sane
+ clear
+ cat <<EOM
+
+There seems to be a problem with the lxdialog companion utility which is
+built prior to running Menuconfig. Usually this is an indicator that you
+have upgraded/downgraded your ncurses libraries and did not remove the
+old ncurses header file(s) in /usr/include or /usr/include/ncurses.
+
+It is VERY important that you have only one set of ncurses header files
+and that those files are properly version matched to the ncurses libraries
+installed on your machine.
+
+You may also need to rebuild lxdialog. This can be done by moving to
+the /usr/src/linux/scripts/lxdialog directory and issuing the
+"make clean all" command.
+
+If you have verified that your ncurses install is correct, you may email
+the author <roadcapw@titus.org> or post a message on the linux.dev.kernel
+news group for additional assistance.
+
+EOM
+ cleanup
+ exit 139
+ ;;
+ esac
+ done
+}
+
+#
+# Create a menu item to load an alternate configuration file.
+#
+g_alt_config () {
+ echo -n "get_alt_config 'Load an Alternate Configuration File' "\
+ >>MCmenu
+}
+
+#
+# Get alternate config file name and load the
+# configuration from it.
+#
+get_alt_config () {
+ set -f ## Switch file expansion OFF
+
+ while true
+ do
+ ALT_CONFIG="${ALT_CONFIG:-$_CONFIG}"
+
+ $DIALOG --backtitle "$backtitle" \
+ --inputbox "\
+Enter the name of the configuration file you wish to load. \
+Accept the name shown to restore the configuration you \
+last retrieved. Leave blank to abort."\
+ 11 55 "$ALT_CONFIG" 2>MCdialog.out
+
+ if [ "$?" = "0" ]
+ then
+ ALT_CONFIG=`cat MCdialog.out`
+
+ [ "_" = "_$ALT_CONFIG" ] && break
+
+ if eval [ -r "$ALT_CONFIG" ]
+ then
+ eval load_config_file "$ALT_CONFIG"
+ break
+ else
+ echo -ne "\007"
+ $DIALOG --backtitle "$backtitle" \
+ --infobox "File does not exist!" 3 38
+ sleep 2
+ fi
+ else
+ cat <<EOM >help.out
+
+For various reasons, one may wish to keep several different kernel
+configurations available on a single machine.
+
+If you have saved a previous configuration in a file other than the
+kernel's default, entering the name of the file here will allow you
+to modify that configuration.
+
+If you are uncertain, then you have probably never used alternate
+configuration files. You should therefor leave this blank to abort.
+
+EOM
+ $DIALOG --backtitle "$backtitle"\
+ --title "Load Alternate Configuration"\
+ --textbox help.out $ROWS $COLS
+ fi
+ done
+
+ set +f ## Switch file expansion ON
+ rm -f help.out MCdialog.out
+}
+
+#
+# Create a menu item to store an alternate config file.
+#
+s_alt_config () {
+ echo -n "save_alt_config 'Save Configuration to an Alternate File' "\
+ >>MCmenu
+}
+
+#
+# Get an alternate config file name and save the current
+# configuration to it.
+#
+save_alt_config () {
+ set -f ## Switch file expansion OFF
+
+ while true
+ do
+
+ $DIALOG --backtitle "$backtitle" \
+ --inputbox "\
+Enter a filename to which this configuration should be saved \
+as an alternate. Leave blank to abort."\
+ 10 55 "$ALT_CONFIG" 2>MCdialog.out
+
+ if [ "$?" = "0" ]
+ then
+ ALT_CONFIG=`cat MCdialog.out`
+
+ [ "_" = "_$ALT_CONFIG" ] && break
+
+ if eval touch $ALT_CONFIG 2>/dev/null
+ then
+ eval save_configuration $ALT_CONFIG
+ load_functions ## RELOAD
+ break
+ else
+ echo -ne "\007"
+ $DIALOG --backtitle "$backtitle" \
+ --infobox "Can't create file! Probably a nonexistent directory." 3 60
+ sleep 2
+ fi
+ else
+ cat <<EOM >help.out
+
+For various reasons, one may wish to keep different
+configurations available on a single machine.
+
+Entering a file name here will allow you to later retrieve, modify
+and use the current configuration as an alternate to whatever
+configuration options you have selected at that time.
+
+If you are uncertain what all this means then you should probably
+leave this blank.
+EOM
+ $DIALOG --backtitle "$backtitle"\
+ --title "Save Alternate Configuration"\
+ --textbox help.out $ROWS $COLS
+ fi
+ done
+
+ set +f ## Switch file expansion ON
+ rm -f help.out MCdialog.out
+}
+
+
+#
+# Load config file into the environment converting all
+# "# OPTION is not set" lines to "OPTION=n".
+#
+# The $ARCH defaults are loaded first so "new"/previously
+# unconfigured parameters are assigned the proper defaults.
+#
+function load_config_file () {
+ eval "`sed -e 's/# \(.*\) is not set.*/\1=n/' dmsdos-config.default $1`"
+}
+
+
+#
+# Just what it says.
+#
+save_configuration () {
+ ${DIALOG} --backtitle "$backtitle" \
+ --infobox "Saving dmsdos configuration..." 3 40
+
+ #
+ # Now, let's redefine the configuration functions for final
+ # output to the config files.
+ #
+ # Nested function definitions, YIPEE!
+ #
+ function bool () {
+ eval define_bool "$2" "\${$2:-n}"
+ }
+
+ function tristate () {
+ eval define_bool "$2" "\${$2:-n}"
+ }
+
+ function dep_tristate () {
+ eval x=\${$2:-n}
+
+ if eval [ "_$3" = "_m" ]
+ then
+ if [ "$x" = "y" ]
+ then
+ x="m"
+ fi
+ fi
+
+ define_bool "$2" "$x"
+ }
+
+ function int () {
+ eval x=\${$2:-"$3"}
+ echo "$2=$x" >>$CONFIG
+ echo "#define $2 ($x)" >>$CONFIG_H
+ }
+
+ function hex () {
+ eval x=\${$2:-"$3"}
+ echo "$2=$x" >>$CONFIG
+ echo "#define $2 0x${x##*[x,X]}" >>$CONFIG_H
+ }
+
+ function define_bool () {
+ eval $1="$2"
+
+ case "$2" in
+ y)
+ echo "$1=y" >>$CONFIG
+ echo "#define $1 1" >>$CONFIG_H
+ ;;
+
+ m)
+ if [ "$CONFIG_MODULES" = "y" ]
+ then
+ echo "$1=m" >>$CONFIG
+ echo "#undef $1" >>$CONFIG_H
+ echo "#define $1_MODULE 1" >>$CONFIG_H
+ else
+ echo "$1=y" >>$CONFIG
+ echo "#define $1 1" >>$CONFIG_H
+ fi
+ ;;
+
+ n)
+ echo "# $1 is not set" >>$CONFIG
+ echo "#undef $1" >>$CONFIG_H
+ ;;
+ esac
+ }
+
+ function choice () {
+ #
+ # Find the first choice that's already set to 'y'
+ #
+ choices="$2"
+ default="$3"
+ current=
+
+ set -- $choices
+ while [ -n "$2" ]
+ do
+ if eval [ "_\$$2" = "_y" ]
+ then
+ current=$1
+ break
+ fi
+ shift ; shift
+ done
+
+ #
+ # Use the default if none were set.
+ #
+ : ${current:=$default}
+
+ #
+ # Then extract the actual option from the list of choices.
+ #
+ current=${choices#*$current} ; set $current
+
+ define_bool "$1" "y"
+ }
+
+ function mainmenu_name () {
+ :
+ }
+
+ function mainmenu_option () {
+ comment_is_option=TRUE
+ }
+
+ function endmenu () {
+ :
+ }
+
+ function comment () {
+ if [ "$comment_is_option" ]
+ then
+ comment_is_option=
+ echo >>$CONFIG
+ echo "#" >>$CONFIG
+ echo "# $1" >>$CONFIG
+ echo "#" >>$CONFIG
+
+ echo >>$CONFIG_H
+ echo "/*" >>$CONFIG_H
+ echo " * $1" >>$CONFIG_H
+ echo " */" >>$CONFIG_H
+ fi
+ }
+
+ DEF_CONFIG="${1:-$_CONFIG}"
+ DEF_CONFIG_H="$AUTOCONF_H"
+
+ CONFIG=.tmpconfig
+ CONFIG_H=.tmpconfig.h
+
+ echo "#" >$CONFIG
+ echo "# Automatically generated by make menuconfig: don't edit" >>$CONFIG
+ echo "#" >>$CONFIG
+
+ echo "/*" >$CONFIG_H
+ echo " * Automatically generated by make menuconfig: don't edit" >>$CONFIG_H
+ echo " * Use make menuconfig instead." >>$CONFIG_H
+ echo " */" >>$CONFIG_H
+
+ MAKE=: #To prevent sound Makefile from running.
+
+ if . $CONFIG_IN >>.menuconfig.log 2>&1
+ then
+ if [ -f "$DEF_CONFIG" ]
+ then
+ rm -f ${DEF_CONFIG}.old
+ mv $DEF_CONFIG ${DEF_CONFIG}.old
+ fi
+
+ mv $CONFIG $DEF_CONFIG
+ mv $CONFIG_H $DEF_CONFIG_H
+
+ return 0
+ else
+ return 1
+ fi
+}
+
+
+#
+# Remove temporary files
+#
+cleanup () {
+ cleanup1
+ cleanup2
+ stty $S_TERMIO
+}
+
+cleanup1 () {
+ rm -f MCmenu* MCradiolists MCdialog.out help.out
+}
+
+cleanup2 () {
+ rm -f .tmpconfig .tmpconfig.h
+}
+
+set_geometry () {
+ # Some distributions export these with incorrect values
+ # which can really screw up some ncurses programs.
+ LINES= COLUMNS=
+
+ ROWS=${1:-24} COLS=${2:-80}
+
+ # Just in case the nasty rlogin bug returns.
+ #
+ [ $ROWS = 0 ] && ROWS=24
+ [ $COLS = 0 ] && COLS=80
+
+ if [ $ROWS -lt 19 -o $COLS -lt 80 ]
+ then
+ echo -e "\n\007Your display is too small to run Menuconfig!"
+ echo "It must be at least 19 lines by 80 columns."
+ exit 0
+ fi
+
+ ROWS=$((ROWS-4)) COLS=$((COLS-5))
+}
+
+S_TERMIO=`stty -g`
+
+set_geometry `stty size 2>/dev/null`
+
+menu_instructions="\
+Arrow keys navigate the menu. \
+<Enter> selects submenus --->. \
+Highlighted letters are hotkeys. \
+Pressing <Y> includes, <N> excludes features. \
+Press <Esc><Esc> to exit, <?> for Help. \
+Legend: [*] built-in [ ] excluded "
+
+radiolist_instructions="\
+Use the arrow keys to navigate this window or \
+press the hotkey of the item you wish to select \
+followed by the <SPACE BAR>.
+Press <?> for additional information about this option."
+
+inputbox_instructions_int="\
+Please enter a decimal value. \
+Fractions will not be accepted. \
+Use the <TAB> key to move from the input field to the buttons below it."
+
+inputbox_instructions_hex="\
+Please enter a hexadecimal value. \
+Use the <TAB> key to move from the input field to the buttons below it."
+
+DIALOG="${DIALOG:-/usr/src/linux/scripts/lxdialog/lxdialog}"
+
+if [ ! -x $DIALOG ];
+then
+ echo "Compiling lxdialog in kernel source tree..."
+ make -C /usr/src/linux/scripts/lxdialog lxdialog
+ if [ "$?" != "0" ];
+ then
+ echo "Something's wrong here. Sorry, I don't know what."
+ exit 1
+ fi
+fi
+
+backtitle="Dmsdos CVF-FAT Module Configuration"
+
+trap "cleanup ; rm -f .menuconfig ; exit 1" 1 2 15
+
+
+#
+# Locate default files.
+#
+DEFAULTS="dmsdos-config.default"
+
+CONFIG_IN="${1:-./Config.in}"
+
+_CONFIG="${2:-.config}"
+
+AUTOCONF_H="dmsdos-config.h"
+
+if [ -f "$_CONFIG" ]; then
+ DEFAULTS=$_CONFIG
+fi
+
+if [ -f $DEFAULTS ]
+then
+ echo
+ echo "Using defaults found in" $DEFAULTS
+ load_config_file $DEFAULTS
+else
+ echo
+ echo "No defaults found"
+fi
+
+# Fresh new log.
+>.menuconfig.log
+
+$DIALOG --backtitle "$backtitle" \
+ --infobox "Preparing configuration scripts..." 3 40
+
+# Load the functions used by the config.in files.
+load_functions
+
+#
+# Read config.in files and parse them into one shell function per menu.
+#
+parse_config_files $CONFIG_IN
+
+#
+# Start the ball rolling from the top.
+#
+activate_menu MCmenu0
+
+#
+# All done!
+#
+cleanup1
+
+#
+# Confirm and Save
+#
+if $DIALOG --backtitle "$backtitle" \
+ --yesno "Do you wish to save your new dmsdos configuration?" 5 60
+
+then
+ save_configuration
+ stty $S_TERMIO
+ clear
+
+ cat <<EOM
+
+
+The dmsdos module is now configured. Do a 'make clean; make' to (re)compile.
+
+EOM
+else
+ clear
+ stty $S_TERMIO
+ echo -e "Your dmsdos configuration changes were NOT saved.\n"
+ exit 1
+fi
+
+
+exit 0
+
diff --git a/src/cctest.c b/src/cctest.c
new file mode 100644
index 0000000..ba88500
--- /dev/null
+++ b/src/cctest.c
@@ -0,0 +1,14 @@
+#include<stdio.h>
+
+void main(void)
+{ int x=1;
+ char*p=(char*)&x;
+
+ printf("sizeof(char)=%d\n",sizeof(char));
+ printf("sizeof(int)=%d\n",sizeof(int));
+ printf("sizeof(short)=%d\n",sizeof(short));
+ printf("sizeof(long)=%d\n",sizeof(long));
+
+ if(*p==0)printf("big endian\n");
+ else printf("little endian\n");
+} \ No newline at end of file
diff --git a/src/check.sh b/src/check.sh
new file mode 100644
index 0000000..e26da29
--- /dev/null
+++ b/src/check.sh
@@ -0,0 +1,92 @@
+#!/bin/sh
+
+echo "Checking kernel configuration..."
+
+if [ ! -f /usr/src/linux/Makefile ];
+then
+ echo "Hmm, no Linux sources found in /usr/src/linux"
+ exit 0
+fi
+
+head -3 /usr/src/linux/Makefile | tr -d " " > /tmp/help.$$
+source /tmp/help.$$
+rm /tmp/help.$$
+VPS=`echo "$VERSION.$PATCHLEVEL.$SUBLEVEL"`
+VP=`echo "$VERSION.$PATCHLEVEL"`
+
+if [ ! -f /usr/src/linux/.config ];
+then
+ echo "Hmm, kernel is not configured."
+ exit 0
+fi
+
+CONFIG_MODULES=n
+CONFIG_BLK_DEV_LOOP=n
+CONFIG_FAT_FS=n
+CONFIG_UMSDOS_FS=n
+
+source /usr/src/linux/.config
+
+WARN=n
+
+if [ "$CONFIG_MODULES" = "n" -o "$CONFIG_BLK_DEV_LOOP" = "n" -o "$CONFIG_FAT_FS" = "n" ];
+then
+ echo
+ echo "WARNING: Your kernel is not configured correctly for dmsdos."
+ echo "The following feature(s) is(are) missing:"
+if [ "$CONFIG_MODULES" = "n" ];
+then
+ echo " - loadable module support"
+fi
+if [ "$CONFIG_BLK_DEV_LOOP" = "n" ];
+then
+ echo " - loopback block device"
+fi
+if [ "$CONFIG_FAT_FS" = "n" ];
+then
+ echo " - fat filesystem"
+fi
+ echo "Please correct this later since dmsdos won't run without."
+ WARN=y
+fi
+
+if [ "$VP" = "2.1" -a "$CONFIG_UMSDOS_FS" != "n" -a "$SUBLEVEL" -lt 94 ];
+then
+ echo
+ echo "ERROR: you have enabled UMSDOS in $VPS kernel."
+ echo "This configuration cannot work due to incompatibility."
+ echo "Please upgrade your kernel to at least 2.1.94 or disable UMSDOS."
+ echo "Please read the documentation for details."
+ exit 1
+fi
+
+if [ "$VP" = "2.0" -a "$SUBLEVEL" -lt 29 ];
+then
+ echo
+ echo "WARNING: your kernel version $VPS is horribly outdated."
+ echo "This configuration has never been tested. If it fails please"
+ echo "upgrade your kernel to 2.0.29 or newer."
+ echo "Please read the documentation for details."
+ WARN=y
+fi
+
+if [ "$VP" = "2.1" -a "$SUBLEVEL" -lt 76 ];
+then
+ echo
+ echo "ERROR: your kernel version $VPS is too old for CVF-FAT."
+ echo "This configuration is unsupported."
+ echo "Please upgrade your kernel to 2.1.94 or newer or use 2.0.xx (xx>=29)."
+ echo "Please read the documentation for details."
+ exit 1
+fi
+
+if [ "$WARN" = "y" ];
+then
+ echo
+ echo "Press ENTER to continue."
+ read junk
+else
+ echo "kernel configuration is OK."
+fi
+
+exit 0
diff --git a/src/cmpdisk.README b/src/cmpdisk.README
new file mode 100644
index 0000000..bace4de
--- /dev/null
+++ b/src/cmpdisk.README
@@ -0,0 +1,3 @@
+cmpdisk is a DOS program. See doc/testing.doc for details. The sources are in
+cmpdisk.cpp. You must compile them under DOS, preferrably with a 16 bit dos
+C compiler. The code was written and tested under Borland C++ 3.1.
diff --git a/src/cmpdisk.cpp b/src/cmpdisk.cpp
new file mode 100644
index 0000000..d2335c7
--- /dev/null
+++ b/src/cmpdisk.cpp
@@ -0,0 +1,131 @@
+
+/* BC 3.1 source code for something similar to simple diff */
+
+#include <string.h>
+#include <dos.h>
+#include <dir.h>
+#include <stdio.h>
+#include <io.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+
+#define COMPAREBUFLEN (1024*16)
+char comparebuf1[COMPAREBUFLEN];
+char comparebuf2[COMPAREBUFLEN];
+
+int TestAllFiles(char *path1,char *path2)
+{
+ int old_path_len1,old_path_len2;
+ int done,file1,file2,i;
+ struct ffblk s;
+ int len1,len2;
+ long int pos;
+
+ old_path_len1=strlen(path1);
+ old_path_len2=strlen(path2);
+ strcat(path1,"*.*");
+ done=findfirst(path1,&s,0x2f);
+ path1[old_path_len1]=0;
+
+ while(!done)
+ {
+ path1[old_path_len1]=0;
+ path2[old_path_len2]=0;
+ strcat(path1,s.ff_name);
+ strcat(path2,s.ff_name);
+
+ printf("File in progress %s",path1);
+
+ if ((file1=open(path1, O_RDONLY | O_BINARY , S_IREAD))==-1)
+ {printf("\nCannot open file: %s \n",path1);goto error;}
+ else if ((file2=open(path2, O_RDONLY | O_BINARY , S_IREAD))==-1)
+ {printf("\nCannot open file: %s \n",path2);close(file1);goto error;}
+ else
+ {
+ len2=len1=COMPAREBUFLEN;pos=0;
+ while((len1==COMPAREBUFLEN)&&(len2==COMPAREBUFLEN))
+ {
+ if((len1=read(file1,comparebuf1,COMPAREBUFLEN))==-1)
+ {
+ printf("\ndisk IO error!: cannot read file %s\n",path1);
+ goto error_close;
+ }
+ if((len2=read(file2,comparebuf2,COMPAREBUFLEN))==-1)
+ {
+ printf("\ndisk IO error!: cannot read file %s\n",path2);
+ goto error_close;
+ };
+ if(len1) if(memcmp(comparebuf1,comparebuf2,len1))
+ {
+ for(i=0;(i<len1)&&(*(comparebuf1+i)==*(comparebuf2+i));i++);
+ printf("\nCompare Error %s on pos %X!\n",path1,pos+i);
+ goto error_close;
+ }
+ pos+=len1;
+ }
+ if(len1!=len2)
+ {
+ printf("\nDifferent lengths of files %s and %s",path1,path2);
+ error_close:
+ close(file2);
+ close(file1);
+ error:
+ if(getchar()=='q') return(1);
+ break;
+ }else{
+ printf(" ... compared %ld bytes\n",pos);
+ close(file2);
+ close(file1);
+ };
+ }
+ done=findnext(&s);
+ }
+
+ path1[old_path_len1]=0;
+ path2[old_path_len2]=0;
+ strcat(path1,"*.*");
+ done=findfirst(path1,&s,FA_DIREC | 0x7);
+ path1[old_path_len1]=0;
+
+
+ while(!done)
+ {
+ if(*s.ff_name != '.')
+ {
+ if((s.ff_attrib & FA_DIREC)!=0)
+ {
+ strcat(path1,s.ff_name);
+ strcat(path1,"\\");
+ path1[old_path_len1+strlen(s.ff_name)+1]=0;
+ strcat(path2,s.ff_name);
+ strcat(path2,"\\");
+ path1[old_path_len2+strlen(s.ff_name)+1]=0;
+ TestAllFiles(path1,path2);
+ path1[old_path_len1]=0;
+ path2[old_path_len2]=0;
+ }
+ }
+ done=findnext(&s);
+ }
+ path1[old_path_len1]=0;
+ path2[old_path_len2]=0;
+
+ return 0;
+}
+
+int main(int argc,char *argv[])
+{
+ if(argc<3)
+ {
+ printf("usage: cmpdisk <d>: <d>:\n");
+ return(0);
+ };
+
+ {
+ char path1[255],path2[255];
+ strcpy(path1,argv[1]);
+ strcpy(path2,argv[2]);
+ TestAllFiles(path1,path2);
+ };
+ return(0);
+}; \ No newline at end of file
diff --git a/src/conf-wrapper.sh b/src/conf-wrapper.sh
new file mode 100644
index 0000000..daa1d4a
--- /dev/null
+++ b/src/conf-wrapper.sh
@@ -0,0 +1,14 @@
+#/bin/sh
+
+echo
+echo "dmsdos seems not configured. I'm going to call dmsdos configuration now."
+echo "If you do not want me to do this, press CTRL-C. Press Enter to continue."
+read junk
+
+CT=config
+if [ -f /usr/src/linux/scripts/lxdialog/lxdialog ];
+then
+CT=menuconfig
+fi
+
+make $CT
diff --git a/src/config.debug b/src/config.debug
new file mode 100644
index 0000000..966307d
--- /dev/null
+++ b/src/config.debug
@@ -0,0 +1,62 @@
+#
+# Automatically generated by make menuconfig: don't edit
+#
+
+#
+# CVF formats to be supported
+#
+DMSDOS_CONFIG_DBLSP_DRVSP=y
+DMSDOS_CONFIG_DRVSP3=y
+DMSDOS_CONFIG_STAC3=y
+DMSDOS_CONFIG_STAC4=y
+
+#
+# Memory Management
+#
+# USE_XMALLOC is not set
+USE_VMALLOC=y
+
+#
+# Cache setup
+#
+LISTSIZE=1024
+MDFATCACHESIZE=40
+DFATCACHESIZE=20
+BITFATCACHESIZE=10
+MAX_CACHE_TIME=60
+CCACHESIZE=64
+MAX_CCACHE_TIME=240
+
+#
+# Read-ahead Options
+#
+MAX_READA=64
+# USE_READA_LIST is not set
+READA_THRESHOLD=4095
+
+#
+# Misc Options
+#
+DBL_WRITEACCESS=y
+DEFAULT_LOGLEVEL=0
+# NOLOG is not set
+DEFAULT_CF=11
+SEQLOG=y
+DMSDOS_USE_READPAGE=y
+
+#
+# Internal Compression Daemon
+#
+# INTERNAL_DAEMON is not set
+
+#
+# Speedup Tricks & Hacks
+#
+SP_BIT0=y
+SP_BIT1=y
+# SP_BIT2 is not set
+SP_BIT3=y
+# SP_BIT4 is not set
+# SP_BIT5 is not set
+# SP_BIT6 is not set
+# SP_BIT7 is not set
diff --git a/src/config.high-traffic-write b/src/config.high-traffic-write
new file mode 100644
index 0000000..936c0ea
--- /dev/null
+++ b/src/config.high-traffic-write
@@ -0,0 +1,64 @@
+#
+# Automatically generated by make menuconfig: don't edit
+#
+
+#
+# CVF formats to be supported
+#
+DMSDOS_CONFIG_DBLSP_DRVSP=y
+DMSDOS_CONFIG_DRVSP3=y
+DMSDOS_CONFIG_STAC3=y
+DMSDOS_CONFIG_STAC4=y
+
+#
+# Memory Management
+#
+# USE_XMALLOC is not set
+USE_VMALLOC=y
+
+#
+# Cache setup
+#
+LISTSIZE=1024
+MDFATCACHESIZE=40
+DFATCACHESIZE=20
+BITFATCACHESIZE=10
+MAX_CACHE_TIME=60
+CCACHESIZE=64
+MAX_CCACHE_TIME=240
+
+#
+# Read-ahead Options
+#
+MAX_READA=64
+USE_READA_LIST=y
+READA_LIST_SIZE=256
+READA_THRESHOLD=4095
+
+#
+# Misc Options
+#
+DBL_WRITEACCESS=y
+DEFAULT_LOGLEVEL=0
+# NOLOG is not set
+DEFAULT_CF=11
+# SEQLOG is not set
+DMSDOS_USE_READPAGE=y
+
+#
+# Internal Compression Daemon
+#
+INTERNAL_DAEMON=y
+IDMSDOSD_TIME=30
+
+#
+# Speedup Tricks & Hacks
+#
+SP_BIT0=y
+SP_BIT1=y
+SP_BIT2=y
+# SP_BIT3 is not set
+SP_BIT4=y
+SP_BIT5=y
+# SP_BIT6 is not set
+SP_BIT7=y
diff --git a/src/config.low-traffic-write b/src/config.low-traffic-write
new file mode 100644
index 0000000..5140819
--- /dev/null
+++ b/src/config.low-traffic-write
@@ -0,0 +1,64 @@
+#
+# Automatically generated by make menuconfig: don't edit
+#
+
+#
+# CVF formats to be supported
+#
+DMSDOS_CONFIG_DBLSP_DRVSP=y
+DMSDOS_CONFIG_DRVSP3=y
+DMSDOS_CONFIG_STAC3=y
+DMSDOS_CONFIG_STAC4=y
+
+#
+# Memory Management
+#
+# USE_XMALLOC is not set
+USE_VMALLOC=y
+
+#
+# Cache setup
+#
+LISTSIZE=1024
+MDFATCACHESIZE=40
+DFATCACHESIZE=20
+BITFATCACHESIZE=10
+MAX_CACHE_TIME=60
+CCACHESIZE=64
+MAX_CCACHE_TIME=240
+
+#
+# Read-ahead Options
+#
+MAX_READA=64
+USE_READA_LIST=y
+READA_LIST_SIZE=256
+READA_THRESHOLD=4095
+
+#
+# Misc Options
+#
+DBL_WRITEACCESS=y
+DEFAULT_LOGLEVEL=0
+# NOLOG is not set
+DEFAULT_CF=11
+# SEQLOG is not set
+DMSDOS_USE_READPAGE=y
+
+#
+# Internal Compression Daemon
+#
+INTERNAL_DAEMON=y
+IDMSDOSD_TIME=30
+
+#
+# Speedup Tricks & Hacks
+#
+SP_BIT0=y
+SP_BIT1=y
+# SP_BIT2 is not set
+# SP_BIT3 is not set
+SP_BIT4=y
+SP_BIT5=y
+# SP_BIT6 is not set
+# SP_BIT7 is not set
diff --git a/src/config.small-module b/src/config.small-module
new file mode 100644
index 0000000..36fb7e3
--- /dev/null
+++ b/src/config.small-module
@@ -0,0 +1,63 @@
+#
+# Automatically generated by make menuconfig: don't edit
+#
+
+#
+# CVF formats to be supported
+#
+DMSDOS_CONFIG_DBLSP_DRVSP=y
+DMSDOS_CONFIG_DRVSP3=y
+DMSDOS_CONFIG_STAC3=y
+DMSDOS_CONFIG_STAC4=y
+
+#
+# Memory Management
+#
+# USE_XMALLOC is not set
+USE_VMALLOC=y
+
+#
+# Cache setup
+#
+LISTSIZE=1024
+MDFATCACHESIZE=40
+DFATCACHESIZE=20
+BITFATCACHESIZE=10
+MAX_CACHE_TIME=60
+CCACHESIZE=64
+MAX_CCACHE_TIME=240
+
+#
+# Read-ahead Options
+#
+MAX_READA=64
+USE_READA_LIST=y
+READA_LIST_SIZE=256
+READA_THRESHOLD=4095
+
+#
+# Misc Options
+#
+DBL_WRITEACCESS=y
+DEFAULT_LOGLEVEL=0
+NOLOG=y
+DEFAULT_CF=11
+# SEQLOG is not set
+DMSDOS_USE_READPAGE=y
+
+#
+# Internal Compression Daemon
+#
+# INTERNAL_DAEMON is not set
+
+#
+# Speedup Tricks & Hacks
+#
+SP_BIT0=y
+SP_BIT1=y
+# SP_BIT2 is not set
+SP_BIT3=y
+SP_BIT4=y
+SP_BIT5=y
+# SP_BIT6 is not set
+SP_BIT7=y
diff --git a/src/cvflist.c b/src/cvflist.c
new file mode 100644
index 0000000..ca86cac
--- /dev/null
+++ b/src/cvflist.c
@@ -0,0 +1,264 @@
+/*
+cvflist.c
+
+DMSDOS: utility that lists the names of CVFs inside a FAT12 or FAT16 MSDOS fs
+
+******************************************************************************
+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>
+#include<asm/unaligned.h>
+#include<asm/types.h>
+#include<unistd.h>
+#include<fcntl.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)
+
+#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 */
+
+struct buffer_head* raw_bread(struct super_block*sb,int block)
+{ struct buffer_head*bh;
+ int fd=sb->s_dev;
+
+ if(lseek(fd,block*512,SEEK_SET)<0)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;
+
+ if(read(fd,bh->b_data,512)==512)return bh;
+
+ free(bh->b_data);
+ free(bh);
+
+ return NULL;
+}
+
+void raw_brelse(struct super_block*sb,struct buffer_head*bh)
+{ if(bh==NULL)return;
+ free(bh->b_data);
+ free(bh);
+}
+
+int list_cvfs(struct super_block*sb)
+{ 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)
+ { fprintf(stderr,"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);
+ printf("%s\n",cvfname);
+ }
+ }
+ }
+ raw_brelse(sb,bh);
+ }
+ return 0;
+}
+
+/*okay, first thing is setup super block*/
+/* stolen from fatfs */
+/* Read the super block of an MS-DOS FS. */
+
+int read_super(struct super_block *sb)
+{
+ struct buffer_head *bh;
+ struct fat_boot_sector *b;
+ int data_sectors,logical_sector_size,sector_mult,fat_clusters=0;
+ int error,fat=0;
+ int blksize = 512;
+
+ MSDOS_SB(sb)->cvf_format=NULL;
+ MSDOS_SB(sb)->private_data=NULL;
+
+ blksize = 512;
+
+ bh = raw_bread(sb, 0);
+ if (bh == NULL) {
+ raw_brelse (sb, bh);
+ sb->s_dev = 0;
+ fprintf(stderr,"cannot read file\n");
+ return -1;
+ }
+ 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;
+
+ return list_cvfs(sb);
+
+ c_err:
+ fprintf(stderr,"Not a FAT12 or FAT16 MSDOS filesystem.\n");
+ return -1;
+}
+
+int main(int argc, char*argv[])
+{ struct super_block *sb;
+ int fd;
+ int ret;
+
+ if(argc!=2)
+ { fprintf(stderr,"list names of CVFs in a FAT12 or FAT16 MSDOS fs.\n");
+ fprintf(stderr,"usage: %s filename\n",argv[0]);
+ return -1;
+ }
+
+ fd=open(argv[1],O_RDONLY);
+ if(fd<0)
+ { perror("open");
+ return -1;
+ }
+
+ sb=malloc(sizeof(struct super_block));
+ if(sb==NULL)
+ { fprintf(stderr,"malloc failed\n");
+ close(fd);
+ return -1;
+ }
+
+ sb->s_flags=0;
+ sb->s_flags|=MS_RDONLY;
+ sb->s_dev=fd;
+ sb->directlist=NULL;
+ sb->directlen=NULL;
+
+ ret=read_super(sb);
+ close(fd);
+ free(sb);
+ return ret;
+}
+
diff --git a/src/cvftest.c b/src/cvftest.c
new file mode 100644
index 0000000..ede517c
--- /dev/null
+++ b/src/cvftest.c
@@ -0,0 +1,120 @@
+/*
+cvftest.c
+
+DMSDOS: doublespace/drivespace/stacker CVF identification tool.
+
+******************************************************************************
+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>
+
+int main(int argc, char*argv[])
+{ unsigned char sb[512];
+ int i;
+ FILE*f;
+ char*fn;
+
+ if(argc<2||argc>3)
+ {
+ usage:
+ fprintf(stderr,"usage: %s filename [-v]\n",argv[0]);
+ fprintf(stderr,"detect CVFs according to header\n");
+ fprintf(stderr," -v: be verbose i.e. print result to stdout\n");
+ fprintf(stderr," \"-\" as filename means read data from stdin\n");
+ fprintf(stderr,"exit code: 0 = CVF detected, 1 = no CVF, >1 = some error occured\n");
+ exit(20);
+ }
+
+ fn=argv[1];
+
+ if(argc==3)
+ { if(strcmp(argv[1],"-v")==0)fn=argv[2];
+ else if(strcmp(argv[2],"-v")!=0)goto usage;
+ }
+
+ if(strcmp(fn,"-")==0)f=stdin;
+ else
+ { f=fopen(fn,"rb");
+ if(f==NULL)
+ { perror("open failed");
+ exit(4);
+ }
+ }
+
+ for(i=0;i<512;++i)sb[i]=fgetc(f);
+
+ if(fgetc(f)==EOF)
+ { if(ferror(f))
+ { perror("error reading file");
+ exit(3);
+ }
+ if(argc==3)goto nocvf;
+ return 1;
+ }
+
+ if(argc==2)
+ {
+ if(strncmp(sb+3,"MSDBL6.0",8)==0||strncmp(sb+3,"MSDSP6.0",8)==0
+ ||strncmp(sb,"STACKER",7)==0)return 0;
+
+ return 1;
+ }
+
+ if(strncmp(sb+3,"MSDBL6.0",8)==0||strncmp(sb+3,"MSDSP6.0",8)==0)
+ { if(sb[51]==2&&sb[13]==16)printf("drivespace CVF (version 2)\n");
+ else if((sb[51]==3||sb[51]==0)&&sb[13]==64)printf("drivespace 3 CVF\n");
+ else if(sb[51]<2&&sb[13]==16)printf("doublespace CVF (version 1)\n");
+ else printf("unknown (new? damaged?) doublespace or drivespace CVF\n");
+ return 0;
+ }
+ else if(strncmp(sb,"STACKER",7)==0)
+ { int i;
+ unsigned char b,c;
+ unsigned char * p;
+ int StacVersion;
+
+ /* decode super block */
+ for(i=0x30,p=sb+0x50,b=sb[0x4c];i--;p++)
+ { b=0xc4-b;
+ b=b<0x80?b*2:b*2+1;
+ b^=c=*p;
+ *p=b;b=c;
+ }
+ if(sb[0x4e]!=0xa||sb[0x4f]!=0x1a)
+ printf("unknown (new? damaged?) stacker CVF\n");
+ else
+ { StacVersion=sb[0x60];
+ StacVersion&=0xff;
+ StacVersion|=sb[0x61]<<8;
+ StacVersion&=0xffff;
+ if(StacVersion>=410)printf("stacker version 4 CVF\n");
+ else printf("stacker version 3 CVF\n");
+ }
+ return 0;
+ }
+
+ nocvf:
+ printf("not a known CVF\n");
+ return 1;
+}
diff --git a/src/daemon_actions.c b/src/daemon_actions.c
new file mode 100644
index 0000000..89e15e8
--- /dev/null
+++ b/src/daemon_actions.c
@@ -0,0 +1,301 @@
+/*
+daemon_actions.c
+
+DMSDOS CVF-FAT module: external dmsdos daemon.
+
+******************************************************************************
+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 "dmsdos.h"
+#include<sys/ioctl.h>
+#include<sys/types.h>
+#include<sys/stat.h>
+#include<fcntl.h>
+#include<string.h>
+#include<errno.h>
+#include<signal.h>
+#include<unistd.h>
+
+/* default compression level minus 1 for dmsdosd */
+int cfaktor=11;
+
+int debug=0;
+
+int exit_signaled=0;
+
+#define CONTINUE 0
+#define EXIT_AND_FINISH 1
+#define EXIT_IMMEDIATELY 2
+
+/* time to sleep between two idle calls (in seconds)
+ for slower systems you can use a higher value in order to reduce
+ unnecessary polling */
+#define SLEEPTIME 30
+
+#define PIDFILE "/var/run/dmsdosd.pid"
+
+typedef struct
+{ long val1;
+ long val2;
+ long val3;
+ unsigned char data[32*1024];
+} Cdata;
+
+Cdata cdata;
+Cdata ckdata;
+
+void signal_handler(int a)
+{ if(a==SIGINT)exit_signaled=EXIT_AND_FINISH;
+ if(a==SIGTERM)exit_signaled=EXIT_IMMEDIATELY;
+ /* reactivate again */
+ signal(SIGINT,signal_handler);
+ signal(SIGTERM,signal_handler);
+ signal(SIGUSR1,signal_handler); /* this should just wake up */
+}
+
+void pidfile(void)
+{ struct stat buf;
+ int pid=0;
+ char str[100];
+ FILE*f;
+
+ if(stat(PIDFILE,&buf)>=0)
+ { if(debug)fprintf(stderr,"pidfile exists");
+ f=fopen(PIDFILE,"r");
+ if(f)
+ { fscanf(f,"%d",&pid);
+ fclose(f);
+ sprintf(str,"/proc/%d/stat",pid);
+ if(stat(str,&buf)>=0)
+ { fprintf(stderr,"dmsdosd already running\n");
+ exit(1);
+ }
+ }
+ unlink(PIDFILE);
+ }
+
+ f=fopen(PIDFILE,"w");
+ if(f==NULL)
+ { fprintf(stderr,"cannot write pidfile %s\n",PIDFILE);
+ exit(1);
+ }
+ fprintf(f,"%d\n",getpid());
+ fclose(f);
+}
+
+#include <stdarg.h>
+int printk(const char *fmt, ...)
+{ va_list ap;
+ char buf[500];
+ char*p=buf;
+ int i;
+
+ va_start(ap, fmt);
+ i=vsnprintf(buf, 500, 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,"dmsdosd: %s",p);
+
+ return i;
+}
+
+int errm=0;
+
+int get_and_compress_one(int fd)
+{ int ret;
+ int handle;
+ int length;
+ int size;
+ int method;
+
+ /* get cluster to compress */
+ if(exit_signaled!=CONTINUE)return 2;
+ if(debug)fprintf(stderr,"dmsdosd: Trying to read...\n");
+ ret=ioctl(fd,DMSDOS_D_READ,&cdata);
+ if(ret!=1)
+ { if(debug)fprintf(stderr,"dmsdosd: nothing there - D_READ ioctl returned %d\n",ret);
+ return ret;
+ }
+
+ handle=cdata.val1;
+ length=cdata.val2;
+ size=(length-1)/512+1;
+ method=cdata.val3;
+ if(debug)fprintf(stderr,"dmsdosd: compressing...\n");
+
+ if(method==SD_3||method==SD_4)
+ {
+#ifdef DMSDOS_CONFIG_STAC
+ ret=stac_compress(cdata.data,length,ckdata.data,
+ sizeof(ckdata.data),method,cfaktor);
+#else
+ if(errm==0)
+ { errm=1;
+ fprintf(stderr,"dmsdosd: stacker compression requested, but not compiled in!\n");
+ }
+ ret=-1;
+#endif
+ }
+ else
+ ret=dbl_compress(ckdata.data,cdata.data,size,method,cfaktor)*512;
+
+ if(debug)fprintf(stderr,"dmsdosd: compress %X from %d returned %d\n",
+ method,length,ret);
+ if(ret<0)ret=0; /* compression failed */
+ ckdata.val1=handle;
+ ckdata.val2=ret;
+ if(debug)fprintf(stderr,"dmsdosd: writing...\n");
+ ioctl(fd,DMSDOS_D_WRITE,&ckdata);
+
+ return 1; /* one cluster compressed */
+}
+
+void do_dmsdosd_actions(int fd)
+{
+ /* register dmsdosd */
+ if(debug)fprintf(stderr,"dmsdosd: calling D_ASK...\n");
+ if(ioctl(fd,DMSDOS_D_ASK,getpid()))
+ { fprintf(stderr,"dmsdosd: can't get permissions (internal daemon running?)\n");
+ return;
+ }
+
+ signal(SIGINT,signal_handler);
+ signal(SIGTERM,signal_handler);
+ signal(SIGUSR1,signal_handler); /* this should just wake up */
+
+ do
+ { while(get_and_compress_one(fd)==1);
+ /* don't kill the system performance when nothing to compress */
+ if(exit_signaled==CONTINUE)
+ { if(debug)fprintf(stderr,"dmsdosd: sleeping...\n");
+ sleep(SLEEPTIME);
+ /* throw away long idle mdfat/dfat/bitfat sectors */
+ ioctl(fd,DMSDOS_FREE_IDLE_CACHE,NULL);
+ }
+ }
+ while(exit_signaled==CONTINUE);
+
+ if(debug)fprintf(stderr,"dmsdosd: calling D_EXIT...\n");
+ ioctl(fd,DMSDOS_D_EXIT,NULL);
+
+ if(exit_signaled==EXIT_AND_FINISH)
+ { exit_signaled=CONTINUE;
+ while(get_and_compress_one(fd)==1);
+ }
+
+}
+
+int scan(char*arg)
+{ int w;
+
+ if(strncmp(arg,"0x",2)==0)sscanf(arg+2,"%x",&w);
+ else sscanf(arg,"%d",&w);
+
+ return w;
+}
+
+int main(int argc, char*argv[])
+{ Dblsb dblsb;
+ int fd;
+ int ret;
+
+ if(argc<2||argc>4)
+ {
+ fprintf(stderr,"DMSDOS daemon (C) 1996-1998 Frank Gockel, Pavel Pisa\n");
+ fprintf(stderr,"compiled " __DATE__ " " __TIME__ " under dmsdos version %d.%d.%d%s\n\n",
+ DMSDOS_MAJOR,DMSDOS_MINOR,DMSDOS_ACT_REL,DMSDOS_VLT);
+ fprintf(stderr,"Usage: %s (directory)\n",argv[0]);
+ fprintf(stderr," %s (directory) cf\n",argv[0]);
+ fprintf(stderr," %s (directory) cf debug\n",argv[0]);
+ return 1;
+ }
+
+ if(getuid())
+ { fprintf(stderr,"Sorry, you must be root to run me.\n");
+ exit(1);
+ }
+
+ if(argc==4)debug=1;
+
+ fd=open(argv[1],O_RDONLY);
+ if(fd<0)
+ { perror("open");
+ return 1;
+ }
+
+ if(argc==3)
+ { cfaktor=scan(argv[2])-1;
+ if(cfaktor<0||cfaktor>11)
+ { fprintf(stderr,"cf parameter out of range\n");
+ close(fd);
+ return 1;
+ }
+ }
+
+ /* this hack enables reverse version check */
+ /* it must not be changed in order to recognize incompatible older versions */
+ /* this also depends on s_dcluster being the first record in Dblsb */
+ dblsb.s_dcluster=DMSDOS_VERSION;
+
+ ret=ioctl(fd,DMSDOS_GET_DBLSB,&dblsb);
+ if(ret<0)
+ { printf("This is not a DMSDOS directory.\n");
+ close(fd);
+ return 1;
+ }
+ if(ret!=DMSDOS_VERSION)printf("You are running DMSDOS driver version %d.%d.%d.\n",(ret&0xff0000)>>16,
+ (ret&0x00ff00)>>8,ret&0xff);
+ /*printf("debug: ret=0x%08x\n",ret);*/
+ if(ret!=DMSDOS_VERSION)printf("This program was compiled for DMSDOS version %d.%d.%d",
+ (DMSDOS_VERSION&0xff0000)>>16,(DMSDOS_VERSION&0x00ff00)>>8,DMSDOS_VERSION&0xff);
+ if(ret&0x0f000000)
+ { printf("\nSorry, this program is too old for the actual DMSDOS driver version.\n");
+ close(fd);
+ return 1;
+ }
+ if(ret<0x00000902)
+ { printf("\nSorry, this program requires at least DMSDOS driver version 0.9.2.\n");
+ close(fd);
+ return 1;
+ }
+ if(ret!=DMSDOS_VERSION)printf(" but should still work.\n\n");
+
+
+ if(!debug)
+ { if(fork())exit(0);
+ }
+
+ pidfile();
+
+ do_dmsdosd_actions(fd);
+
+ close(fd);
+
+ unlink(PIDFILE);
+
+ return 0;
+}
diff --git a/src/dblspace_alloc.c b/src/dblspace_alloc.c
new file mode 100644
index 0000000..ab6c18a
--- /dev/null
+++ b/src/dblspace_alloc.c
@@ -0,0 +1,710 @@
+/*
+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>
+#endif
+
+extern unsigned long dmsdos_speedup;
+
+#define NEAR_AREA 512
+/* no change for doublespace, but fixes drivespace 3 problems (was 50) */
+#define BIG_HOLE (dblsb->s_sectperclust*3+2)
+
+/* experimental new memory allocation routines */
+#if defined(USE_XMALLOC) && !defined(__DMSDOS_LIB__)
+
+int told=0;
+
+#include<linux/mm.h>
+#include<linux/malloc.h>
+
+#ifndef MAX_KMALLOC_SIZE
+#define MAX_KMALLOC_SIZE 128*1024
+#endif
+
+void* xmalloc(unsigned long size)
+{ void* result;
+
+ if(size<=MAX_KMALLOC_SIZE)
+ { result=kmalloc(size,GFP_KERNEL);
+ if(result)
+ { /* the xfree routine recognizes kmalloc'd memory due to that it is
+ usually not page-aligned -- we double-check here to be sure */
+ if ( ( ((unsigned long)result) & (PAGE_SIZE-1) ) ==0 )
+ { /* uhhh.... the trick is broken...
+ tell the user and go safe using vmalloc */
+ kfree(result);
+ if(told++)printk(KERN_ERR "DMSDOS: page-aligned memory returned by kmalloc - please disable XMALLOC\n");
+ }
+ else return result;
+ }
+ }
+ result=vmalloc(size);
+ /* again check alignment to be 100% sure */
+ if ( ((unsigned long)result) & (PAGE_SIZE-1) )
+ panic("DMSDOS: vmalloc returned unaligned memory - please disable XMALLOC\n");
+ return result;
+}
+
+void xfree(void * data)
+{ unsigned long address=(unsigned long)data;
+
+ /* we rely on the fact that kmalloc'ed memory is unaligned but
+ vmalloc'ed memory is page-aligned */
+ /* we are causing SERIOUS kernel problems if the wrong routine is called -
+ therefore xmalloc tests kmalloc's return address above */
+ if(address&(PAGE_SIZE-1))kfree(data);
+ else vfree(data);
+}
+#endif
+
+#ifdef __DMSDOS_LIB__
+/* we don't need locking in the library */
+void lock_mdfat_alloc(Dblsb*dblsb) {}
+void unlock_mdfat_alloc(Dblsb*dblsb) {}
+#endif
+
+#ifdef __KERNEL__
+void lock_mdfat_alloc(Dblsb*dblsb)
+{ struct semaphore*sem;
+
+ sem=dblsb->mdfat_alloc_semp;
+ down(sem);
+}
+void unlock_mdfat_alloc(Dblsb*dblsb)
+{ struct semaphore*sem;
+
+ sem=dblsb->mdfat_alloc_semp;
+ up(sem);
+}
+#endif
+
+#ifdef DMSDOS_CONFIG_DBL
+void u_free_cluster_sectors(struct super_block*sb, int clusternr,
+ unsigned long* undo_list)
+{
+ Mdfat_entry mde,dummy,newmde;
+ int newval=0;
+ int i;
+ int sectors;
+ int sectornr;
+ int undo_pnt=0;
+ Dblsb*dblsb=MSDOS_SB(sb)->private_data;
+
+ LOG_ALLOC("DMSDOS: free_cluster_sectors: freeing cluster %d\n",clusternr);
+
+ /* read mdfat entry and clear */
+ newmde.sector_minus_1=0;
+ newmde.size_lo_minus_1=0;
+ newmde.size_hi_minus_1=0;
+ newmde.flags=0;
+ dbl_mdfat_value(sb,clusternr,NULL,&mde);
+ dbl_mdfat_value(sb,clusternr,&newmde,&dummy);
+ sectors=mde.size_lo_minus_1+1;
+ sectornr=mde.sector_minus_1+1;
+ if(mde.flags&2)
+ { if(mde.unknown&2)
+ { /* free fragmented cluster */
+ struct buffer_head*bh;
+ int fragcount;
+ int fragpnt;
+ int sec;
+ int cnt;
+
+ bh=raw_bread(sb,sectornr);
+ if(bh==NULL)
+ { printk(KERN_ERR "DMSDOS: free_cluster_sectors: fragmentation list unreadable in cluster %d\n",
+ clusternr);
+ goto nfree;
+ }
+ fragcount=bh->b_data[0];
+ if(fragcount<=0||fragcount>dblsb->s_sectperclust||
+ bh->b_data[1]!=0||bh->b_data[2]!=0||bh->b_data[3]!=0)
+ { printk(KERN_ERR "DMSDOS: free_cluster_sectors: error in fragmentation list in cluster %d\n",
+ clusternr);
+ raw_brelse(sb,bh);
+ goto nfree;
+ }
+ for(fragpnt=1;fragpnt<=fragcount;++fragpnt)
+ { cnt=bh->b_data[fragpnt*4+3];
+ cnt&=0xff;
+ cnt/=4;
+ cnt+=1;
+ sec=bh->b_data[fragpnt*4];
+ sec&=0xff;
+ sec+=bh->b_data[fragpnt*4+1]<<8;
+ sec&=0xffff;
+ sec+=bh->b_data[fragpnt*4+2]<<16;
+ sec&=0xffffff;
+ sec+=1;
+
+ if(fragpnt==1)
+ { if(sec!=sectornr||cnt!=sectors)
+ { printk(KERN_ERR "DMSDOS: free_cluster_sectors: first fragment wrong in cluster %d\n",
+ clusternr);
+ raw_brelse(sb,bh);
+ goto nfree;
+ }
+ }
+
+ for(i=0;i<cnt;++i)
+ { dbl_bitfat_value(sb,sec+i,&newval);
+ if(undo_list)undo_list[undo_pnt++]=sec+i;
+ }
+ }
+ }
+ else
+ { /* free sectors in bitfat */
+ nfree:
+ for(i=0;i<sectors;++i)
+ { dbl_bitfat_value(sb,sectornr+i,&newval);
+ if(undo_list)undo_list[undo_pnt++]=sectornr+i;
+ }
+ }
+
+ dblsb->s_full=0;
+ }
+ else
+ { LOG_CLUST("DMSDOS: stale MDFAT entry for cluster %d, zeroing.\n",
+ clusternr);
+ }
+
+ if(undo_list)undo_list[undo_pnt]=0;
+}
+
+void free_cluster_sectors(struct super_block*sb, int clusternr)
+{ lock_mdfat_alloc(MSDOS_SB(sb)->private_data);
+ u_free_cluster_sectors(sb,clusternr,NULL);
+ unlock_mdfat_alloc(MSDOS_SB(sb)->private_data);
+}
+#endif
+
+/* for statistics - just for interest */
+int nearfound=0;
+int bigfound=0;
+int exactfound=0;
+int anyfound=0;
+int notfound=0;
+int fragfound=0;
+
+/* this function must be called locked */
+int find_free_bitfat(struct super_block*sb, int sectornr, int size)
+{ int testsek;
+ int i;
+ Dblsb*dblsb=MSDOS_SB(sb)->private_data;
+
+ if(dblsb->s_free_sectors<0||dblsb->s_free_sectors>0x2000000)
+ { printk(KERN_NOTICE "DMSDOS: find_free_bitfat: free sectors=%d, cannot believe this. Counting...\n",
+ dblsb->s_free_sectors);
+ check_free_sectors(sb);
+ printk(KERN_NOTICE "DMSDOS: counted free sectors=%d\n",dblsb->s_free_sectors);
+ }
+
+ /* we needn't try in that case... */
+ if(dblsb->s_free_sectors<size)
+ { if(dblsb->s_full<2)printk(KERN_CRIT "DMSDOS: CVF full.\n");
+ dblsb->s_full=2;
+ return -ENOSPC;
+ }
+
+if(dmsdos_speedup&SP_FAST_BITFAT_ALLOC)
+{ /* new strategy: find any fitting hole beginning with last result */
+
+ testsek=dblsb->s_lastnear;
+ if(testsek<dblsb->s_datastart||testsek>dblsb->s_dataend-size)
+ testsek=dblsb->s_datastart;
+
+ while(testsek<=dblsb->s_dataend-size)
+ { if(dbl_bitfat_value(sb,testsek,NULL))
+ { ++testsek;
+ continue;
+ }
+ i=1;
+ while(i<size&&dbl_bitfat_value(sb,testsek+i,NULL)==0)++i;
+ if(i==size)
+ { ++nearfound;
+ dblsb->s_lastnear=testsek+size;
+ dblsb->s_full=0;
+ return testsek;
+ }
+ testsek+=i;
+ }
+
+ /* not found, continue */
+ goto tryany;
+
+} /* end of new strategy */
+else
+{ /* old strategy: do a search in near environment first */
+
+ if(sectornr==0)sectornr=dblsb->s_lastnear;
+
+ if(sectornr>=dblsb->s_datastart&&sectornr<=dblsb->s_dataend-size)
+ { /* search exactly fitting hole near sectornr */
+ testsek=sectornr;
+ while(testsek<sectornr+NEAR_AREA)
+ { if(dbl_bitfat_value(sb,testsek,NULL))
+ { ++testsek;
+ continue;
+ }
+ i=1;
+ while(i<=size&&dbl_bitfat_value(sb,testsek+i,NULL)==0)++i;
+ if(i==size)
+ { dblsb->s_full=0;
+ ++nearfound;
+ dblsb->s_lastnear=testsek;
+ return testsek;
+ }
+ testsek+=i;
+ }
+ testsek=sectornr;
+ while(testsek>sectornr-NEAR_AREA)
+ { if(dbl_bitfat_value(sb,testsek,NULL))
+ { --testsek;
+ continue;
+ }
+ i=1;
+ while(i<=size&&dbl_bitfat_value(sb,testsek-i,NULL)==0)++i;
+ if(i==size)
+ { dblsb->s_full=0;
+ ++nearfound;
+ dblsb->s_lastnear=testsek-i+1;
+ return testsek-i+1;
+ }
+ testsek-=i;
+ }
+ }
+ /* not found, continue */
+} /* end of old strategy */
+
+ /* search for a big hole */
+ if(dblsb->s_lastbig==-1)goto nobighole;
+
+ testsek=dblsb->s_lastbig;
+ if(testsek<dblsb->s_datastart||testsek+size>dblsb->s_dataend)
+ testsek=dblsb->s_datastart;
+
+ while(testsek<=dblsb->s_dataend-size)
+ { if(dbl_bitfat_value(sb,testsek,NULL))
+ { ++testsek;
+ continue;
+ }
+ i=1;
+ while(i<BIG_HOLE&&dbl_bitfat_value(sb,testsek+i,NULL)==0)++i;
+ if(i==BIG_HOLE)
+ { dblsb->s_full=0;
+ ++bigfound;
+ dblsb->s_lastbig=testsek;
+ return testsek;
+ }
+ testsek+=i;
+ }
+
+ if(dblsb->s_lastbig==0)
+ dblsb->s_lastbig=-1; /*there's no big hole any more*/
+ else
+ dblsb->s_lastbig=0; /* next time try from the beginning */
+
+nobighole:
+
+if((dmsdos_speedup&SP_NO_EXACT_SEARCH)==0)
+{
+ /* search for an exactly fitting hole */
+ /* hmmm... now the search code becomes awfully slow */
+ testsek=dblsb->s_datastart;
+ while(testsek<=dblsb->s_dataend-size)
+ { if(dbl_bitfat_value(sb,testsek,NULL))
+ { ++testsek;
+ continue;
+ }
+ i=1;
+ while(i<=size&&dbl_bitfat_value(sb,testsek+i,NULL)==0)++i;
+ if(i==size)
+ { dblsb->s_full=0;
+ ++exactfound;
+ return testsek;
+ }
+ testsek+=i;
+ }
+}
+
+ if(dblsb->s_full==0)
+ { printk(KERN_CRIT "DMSDOS: CVF almost full or highly fragmented at MDFAT level.\n");
+ dblsb->s_full=1;
+ }
+
+tryany:
+ /* last trial: search for any hole >= size */
+ testsek=dblsb->s_datastart;
+ while(testsek<=dblsb->s_dataend-size)
+ { if(dbl_bitfat_value(sb,testsek,NULL))
+ { ++testsek;
+ continue;
+ }
+ i=1;
+ while(i<size&&dbl_bitfat_value(sb,testsek+i,NULL)==0)++i;
+ if(i==size)
+ { ++anyfound;
+ return testsek;
+ }
+ testsek+=i;
+ }
+
+ /* not found, means disk full or MDFAT too fragmented */
+ ++notfound;
+
+ if(dblsb->s_cvf_version==DRVSP3)
+ { if(dblsb->s_full==0)
+ { printk(KERN_CRIT "DMSDOS: CVF almost full or highly fragmented at MDFAT level.\n");
+ dblsb->s_full=1;
+ }
+ }
+ else /* this is for CVFs that cannot fragment cluster data */
+ { if(dblsb->s_full<2)
+ printk(KERN_CRIT "DMSDOS: CVF full or too fragmented at MDFAT level.\n");
+ dblsb->s_full=2;
+ }
+ return 0;
+}
+
+void log_found_statistics()
+{ printk(KERN_INFO "DMSDOS: free sector finding statistics:\n");
+ printk(KERN_INFO "nearfound=%d bigfound=%d exactfound=%d anyfound=%d fragfound=%d notfound=%d\n",
+ nearfound,bigfound,exactfound,anyfound,fragfound,notfound);
+}
+
+#ifdef DMSDOS_CONFIG_DRVSP3
+int try_fragmented(struct super_block*sb,int anear,int nr,
+ unsigned char*fraglist)
+{
+ int i;
+ int sector=anear;
+ Dblsb*dblsb=MSDOS_SB(sb)->private_data;
+ int again=1;
+ int frags;
+ int cnt;
+
+ /* if you never want dmsdos to write fragmented clusters as a last resort
+ then uncomment the next return statement */
+
+ /* return -ENOSPC; */
+
+ if(dblsb->s_free_sectors<nr)
+ { if(dblsb->s_full<2)printk(KERN_CRIT "DMSDOS: CVF full.\n");
+ dblsb->s_full=2;
+ return -ENOSPC;
+ }
+
+ printk(KERN_DEBUG "DMSDOS: trying to allocate fragmented space...\n");
+ LOG_ALLOC("DMSDOS: try_fragmented: start, anear=%d nr=%d\n",anear,nr);
+
+ if(anear==0)sector=dblsb->s_lastnear;
+
+ if(sector<dblsb->s_datastart||sector>dblsb->s_dataend)
+ { sector=dblsb->s_datastart;
+ again=0;
+ }
+
+ retry:
+ frags=0;
+ fraglist[0]=0;
+ fraglist[1]=0;
+ fraglist[2]=0;
+ fraglist[3]=0;
+ cnt=nr;
+
+ while(cnt>0&&sector<=dblsb->s_dataend)
+ { if(dbl_bitfat_value(sb,sector,NULL))
+ { ++sector;
+ continue;
+ }
+ /* free sector found */
+ i=1;
+ while(dbl_bitfat_value(sb,sector+i,NULL)==0&&i<cnt)++i;
+ /* i=number of free sectors :) */
+ ++frags;
+ fraglist[frags*4]=sector-1;
+ fraglist[frags*4+1]=(sector-1)>>8;
+ fraglist[frags*4+2]=(sector-1)>>16;
+ fraglist[frags*4+3]=(sector-1)>>24;
+ fraglist[frags*4+3]|=(i-1)<<2;
+ fraglist[0]=frags;
+ sector+=i+1;
+ cnt-=i;
+ }
+ if(cnt>0&&again!=0)
+ { sector=dblsb->s_datastart;
+ again=0;
+ goto retry;
+ }
+
+ /* now evaluate the result, check for strange things */
+ if(cnt>0)
+ { if(dblsb->s_full<2)
+ printk(KERN_CRIT "DMSDOS: CVF full (cannot even allocate fragmented space)\n");
+ dblsb->s_full=2;
+ return -ENOSPC;
+ }
+ if(cnt<0)
+ { printk(KERN_ERR "DMSDOS: try_fragmented: cnt<0 ? This is a bug.\n");
+ return -EIO;
+ }
+ if(frags<2||frags>dblsb->s_sectperclust+1)
+ { printk(KERN_ERR "DMSDOS: try_fragmented: frags=%d ? Cannot happen.\n",frags);
+ return -EIO;
+ }
+
+ /* correct statistics */
+ ++fragfound;--notfound;
+ dblsb->s_lastnear=sector;
+ dblsb->s_full=1; /* uhh... 0 might be dangerous... */
+
+ /* fraglist must be written to disk in *any* case in order to
+ still represent a correct filesystem
+ this is handled by dbl_write_cluster to prevent too much overhead */
+
+ LOG_ALLOC("DMSDOS: try_fragmented: success, frags=%d\n",frags);
+ return 0;
+}
+#endif /* DMSDOS_CONFIG_DRVSP3 */
+
+#ifdef DMSDOS_CONFIG_DBL
+/* replaces an existing cluster;
+ this unusual function must be called before rewriting any file cluster;
+ *** size must be known (encoded in mde) ***
+ if fraglist!=NULL fragmented clusters are allowed for drivespace 3
+ returns first sector nr
+ changes mde and fraglist
+*/
+
+#define MAX_UNDO_LIST 70
+
+int dbl_replace_existing_cluster(struct super_block*sb, int cluster,
+ int near_sector,
+ Mdfat_entry*mde,
+ unsigned char*fraglist)
+{ Mdfat_entry old_mde,dummy;
+ int i;
+ int newval;
+ int sector;
+ int old_sector;
+ int old_size;
+ int new_size;
+ unsigned long undo_list[MAX_UNDO_LIST];
+ Dblsb*dblsb=MSDOS_SB(sb)->private_data;
+
+ lock_mdfat_alloc(dblsb);
+
+ LOG_ALLOC("DMSDOS: dbl_replace_existing_cluster cluster=%d near_sector=%d\n",
+ cluster,near_sector);
+ dbl_mdfat_value(sb,cluster,NULL,&old_mde);
+ old_size=old_mde.size_lo_minus_1+1;
+ old_sector=old_mde.sector_minus_1+1;
+ new_size=mde->size_lo_minus_1+1;
+ mde->unknown=0; /* ensure fragmented bit is clear */
+ if(old_mde.flags&2)
+ {
+ /* test whether same length (and not fragmented in drivespace 3) */
+ if(old_size==new_size&&
+ (dblsb->s_cvf_version!=DRVSP3||(old_mde.unknown&2)==0)
+ )
+ { LOG_ALLOC("DMSDOS: dbl_replace_existing_cluster: same length, ok\n");
+ sector=old_sector;
+ goto mdfat_update;
+ }
+ if(dblsb->s_cvf_version==DRVSP3&&(old_mde.unknown&2)!=0&&fraglist!=NULL)
+ { /*old cluster is fragmented and new *is allowed* to be fragmentd */
+ struct buffer_head*bh=raw_bread(sb,old_sector);
+ if(bh)
+ { int fragcount;
+ int cnt;
+ int sec;
+ int fragpnt;
+ int sects;
+ int m_cnt=0;
+ int m_sec=0;
+
+ fragcount=bh->b_data[0];
+ sects=0;
+ if(fragcount<2||fragcount>dblsb->s_sectperclust+1||
+ bh->b_data[1]!=0||bh->b_data[2]!=0||bh->b_data[3]!=0
+ )
+ { raw_brelse(sb,bh);
+ goto check_failed;
+ }
+
+ for(fragpnt=1;fragpnt<=fragcount;++fragpnt)
+ { cnt=bh->b_data[fragpnt*4+3];
+ cnt&=0xff;
+ cnt/=4;
+ cnt+=1;
+ sec=bh->b_data[fragpnt*4];
+ sec&=0xff;
+ sec+=bh->b_data[fragpnt*4+1]<<8;
+ sec&=0xffff;
+ sec+=bh->b_data[fragpnt*4+2]<<16;
+ sec&=0xffffff;
+ sec+=1;
+
+ if(fragpnt==1)
+ { m_cnt=cnt;
+ m_sec=sec;
+ if(sec!=old_mde.sector_minus_1+1||cnt!=old_mde.size_lo_minus_1+1)
+ { printk(KERN_ERR "DMSDOS: dbl_replace_existing_cluster: checking old fraglist: first fragment wrong in cluster %d\n",
+ cluster);
+ raw_brelse(sb,bh);
+ goto check_failed;
+ }
+ }
+
+ sects+=cnt;
+ }
+ raw_brelse(sb,bh);
+ if(sects-1/*subtract space for fraglist*/==new_size)
+ { /* we can reuse it */
+ memcpy(fraglist,bh->b_data,4*(fragcount+1));
+ mde->unknown|=2;
+ mde->size_lo_minus_1=m_cnt-1;
+ sector=m_sec;
+ LOG_ALLOC("DMSDOS: dbl_replace_existing_cluster: same fragmented size, ok.\n");
+ goto mdfat_update;
+ }
+ check_failed: ; /*Win32 compiler wants a semicolon here */
+ /* fall through */
+ }
+ }
+ /* different length, replace mdfat entry */
+ newval=0;
+ LOG_ALLOC("DMSDOS: dbl_replace_existing_cluster: freeing old sectors...\n");
+ u_free_cluster_sectors(sb,cluster,undo_list);
+ LOG_ALLOC("DMSDOS: dbl_replace_existing_cluster: freeing finished\n");
+ }
+ LOG_ALLOC("DMSDOS: dbl_replace_existing_cluster: call find_free_bitfat...\n");
+ sector=find_free_bitfat(sb,near_sector,new_size);
+ LOG_ALLOC("DMSDOS: dbl_replace_existing_cluster: find_free_bitfat returned %d\n",
+ sector);
+ if(sector<=0)
+ {
+#ifdef DMSDOS_CONFIG_DRVSP3
+ if(dblsb->s_cvf_version==DRVSP3&&fraglist!=NULL
+ &&(dmsdos_speedup&SP_NO_FRAG_WRITE)==0)
+ { i=try_fragmented(sb,near_sector,new_size+1,fraglist);/*yes one sector more*/
+ if(i==0) /* success */
+ { /* scan fraglist */
+ int frags;
+ int seccount;
+ int usector;
+ int j;
+
+ frags=fraglist[0];
+ for(i=1;i<=frags;++i)
+ { seccount=fraglist[i*4+3];
+ seccount&=0xff;
+ seccount/=4;
+ seccount+=1;
+ usector=fraglist[i*4];
+ usector&=0xff;
+ usector+=fraglist[i*4+1]<<8;
+ usector&=0xffff;
+ usector+=fraglist[i*4+2]<<16;
+ usector&=0xffffff;
+ usector+=1;
+
+ if(i==1) /* note values for mdfat */
+ { mde->size_lo_minus_1=seccount-1;
+ sector=usector;
+ }
+
+ /* allocate in bitfat */
+ newval=1;
+ for(j=0;j<seccount;++j)
+ { /* check whether sectors are really free */
+ if(dbl_bitfat_value(sb,usector+j,NULL))
+ { printk(KERN_EMERG "DMSDOS: try_fragmented returned non-free sectors!\n");
+ /* WARNING: bitfat is corrupt now */
+ unlock_mdfat_alloc(dblsb);
+ panic("DMSDOS: dbl_replace_existing_cluster: This is a bug - reboot and check filesystem\n");
+ return -EIO;
+ }
+ dbl_bitfat_value(sb,usector+j,&newval);
+ }
+ }
+ mde->unknown|=2; /* set fragmented bit */
+ goto mdfat_update;
+ }
+ /* try_fragmented failed: fall through */
+ }
+#endif /* DMSDOS_CONFIG_DRVSP3 */
+ if(old_mde.flags&2)
+ { /* undo bitfat free */
+ LOG_ALLOC("DMSDOS: dbl_replace_existing_cluster: undoing bitfat free...\n");
+ newval=1;
+ for(i=0;undo_list[i]!=0;++i)
+ dbl_bitfat_value(sb,undo_list[i],&newval);
+ }
+ unlock_mdfat_alloc(dblsb);
+ return -ENOSPC; /* disk full */
+ }
+ /* check whether really free (bug supposed in find_free_bitfat) */
+ for(i=0;i<new_size;++i)
+ { if(dbl_bitfat_value(sb,sector+i,NULL))
+ { printk(KERN_EMERG "DMSDOS: find_free_bitfat returned sector %d size %d but they are not all free!\n",
+ sector,new_size);
+ unlock_mdfat_alloc(dblsb);
+ panic("DMSDOS: dbl_replace_existing_cluster: This is a bug - reboot and check filesystem\n");
+ return -EIO;
+ }
+ }
+ newval=1;
+ LOG_ALLOC("DMSDOS: dbl_replace_existing_cluster: allocating in bitfat...\n");
+ for(i=0;i<new_size;++i)dbl_bitfat_value(sb,sector+i,&newval);
+
+mdfat_update:
+ mde->sector_minus_1=sector-1;
+ mde->flags|=2;
+ LOG_ALLOC("DMSDOS: dbl_replace_existing_cluster: writing mdfat...\n");
+ dbl_mdfat_value(sb,cluster,mde,&dummy);
+ unlock_mdfat_alloc(dblsb);
+ return sector; /* okay */
+}
+#endif
diff --git a/src/dblspace_buffer.c b/src/dblspace_buffer.c
new file mode 100644
index 0000000..9ce6288
--- /dev/null
+++ b/src/dblspace_buffer.c
@@ -0,0 +1,195 @@
+/*
+dblspace_buffer.c
+
+DMSDOS CVF-FAT module: low-level buffered read-write 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.
+******************************************************************************
+
+*/
+
+#ifndef __KERNEL__
+#error This file needs __KERNEL__
+#endif
+
+#include <linux/fs.h>
+#include <linux/msdos_fs.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/stat.h>
+#include <linux/mm.h>
+#include <linux/malloc.h>
+#include "dmsdos.h"
+
+/* This is just cut'n'paste from the fat fs original :)
+ We don't do a virtual sector translation here */
+
+struct buffer_head *raw_bread (
+ struct super_block *sb,
+ int block)
+{
+ struct buffer_head *ret = NULL;
+
+ /* Note that the blocksize is 512 or 1024, but the first read
+ is always of size 1024. Doing readahead may be counterproductive
+ or just plain wrong. */
+ if (sb->s_blocksize == 512) {
+ ret = bread (sb->s_dev,block,512);
+ } else {
+ struct buffer_head *real = bread (sb->s_dev,block>>1,1024);
+
+ if (real != NULL){
+ ret = (struct buffer_head *)
+ kmalloc (sizeof(struct buffer_head), GFP_KERNEL);
+ if (ret != NULL) {
+ /* #Specification: msdos / strategy / special device / dummy blocks
+ Many special device (Scsi optical disk for one) use
+ larger hardware sector size. This allows for higher
+ capacity.
+
+ Most of the time, the MsDOS file system that sit
+ on this device is totally unaligned. It use logically
+ 512 bytes sector size, with logical sector starting
+ in the middle of a hardware block. The bad news is
+ that a hardware sector may hold data own by two
+ different files. This means that the hardware sector
+ must be read, patch and written almost all the time.
+
+ Needless to say that it kills write performance
+ on all OS.
+
+ Internally the linux msdos fs is using 512 bytes
+ logical sector. When accessing such a device, we
+ allocate dummy buffer cache blocks, that we stuff
+ with the information of a real one (1k large).
+
+ This strategy is used to hide this difference to
+ the core of the msdos fs. The slowdown is not
+ hidden though!
+ */
+ /*
+ The memset is there only to catch errors. The msdos
+ fs is only using b_data
+ */
+ memset (ret,0,sizeof(*ret));
+ ret->b_data = real->b_data;
+ if (block & 1) ret->b_data += 512;
+ ret->b_next = real;
+ }else{
+ brelse (real);
+ }
+ }
+ }
+ return ret;
+}
+struct buffer_head *raw_getblk (
+ struct super_block *sb,
+ int block)
+{
+ struct buffer_head *ret = NULL;
+ if (sb->s_blocksize == 512){
+ ret = getblk (sb->s_dev,block,512);
+ }else{
+ /* #Specification: msdos / special device / writing
+ A write is always preceded by a read of the complete block
+ (large hardware sector size). This defeat write performance.
+ There is a possibility to optimize this when writing large
+ chunk by making sure we are filling large block. Volunteer ?
+ */
+ ret = raw_bread (sb,block);
+ }
+ return ret;
+}
+
+void raw_brelse (
+ struct super_block *sb,
+ struct buffer_head *bh)
+{
+ if (bh != NULL){
+ if (sb->s_blocksize == 512){
+ brelse (bh);
+ }else{
+ brelse (bh->b_next);
+ /* We can free the dummy because a new one is allocated at
+ each fat_getblk() and fat_bread().
+ */
+ kfree (bh);
+ }
+ }
+}
+
+void raw_mark_buffer_dirty (
+ struct super_block *sb,
+ struct buffer_head *bh,
+ int dirty_val)
+{
+
+#ifdef DBL_WRITEACCESS
+ if (sb->s_blocksize != 512){
+ bh = bh->b_next;
+ }
+ mark_buffer_dirty (bh,dirty_val);
+#else
+printk(KERN_NOTICE "DMSDOS: write access not compiled in, ignored\n");
+#endif
+
+}
+
+void raw_set_uptodate (
+ struct super_block *sb,
+ struct buffer_head *bh,
+ int val)
+{
+ if (sb->s_blocksize != 512){
+ bh = bh->b_next;
+ }
+ mark_buffer_uptodate(bh, val);
+}
+int raw_is_uptodate (
+ struct super_block *sb,
+ struct buffer_head *bh)
+{
+ if (sb->s_blocksize != 512){
+ bh = bh->b_next;
+ }
+ return buffer_uptodate(bh);
+}
+
+/* we really need this for read-ahead */
+void raw_ll_rw_block (
+ struct super_block *sb,
+ int opr,
+ int nbreq,
+ struct buffer_head *bh[32])
+{
+ if (sb->s_blocksize == 512){
+ ll_rw_block(opr,nbreq,bh);
+ }else{
+ struct buffer_head *tmp[32];
+ int i;
+ for (i=0; i<nbreq; i++){
+ tmp[i] = bh[i]->b_next;
+ }
+ ll_rw_block(opr,nbreq,tmp);
+ }
+}
diff --git a/src/dblspace_chk.c b/src/dblspace_chk.c
new file mode 100644
index 0000000..fe28d75
--- /dev/null
+++ b/src/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/dblspace_compr.c b/src/dblspace_compr.c
new file mode 100644
index 0000000..e403d58
--- /dev/null
+++ b/src/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/dblspace_dec.c b/src/dblspace_dec.c
new file mode 100644
index 0000000..cec2d76
--- /dev/null
+++ b/src/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/dblspace_fileops.c b/src/dblspace_fileops.c
new file mode 100644
index 0000000..fb37e8a
--- /dev/null
+++ b/src/dblspace_fileops.c
@@ -0,0 +1,42 @@
+/*
+dblspace_fileops.c
+
+DMSDOS CVF-FAT module: file operation 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.
+******************************************************************************
+
+*/
+
+/* UUHHHH .... too much has changed in 2.1.xx... we need a complete rewrite */
+
+#include"dmsdos.h"
+
+#ifdef __FOR_KERNEL_2_3_10
+ #include "dblspace_fileops.c-2.3.10"
+#else
+ #ifdef __FOR_KERNEL_2_1_80
+ #include "dblspace_fileops.c-2.1.80"
+ #else
+ #include "dblspace_fileops.c-2.0.33"
+ #endif
+#endif
+
diff --git a/src/dblspace_fileops.c-2.0.33 b/src/dblspace_fileops.c-2.0.33
new file mode 100644
index 0000000..eff9e76
--- /dev/null
+++ b/src/dblspace_fileops.c-2.0.33
@@ -0,0 +1,660 @@
+/*
+dblspace_fileops.c-2.0.33
+
+DMSDOS CVF-FAT module: file operation routines (for kernel 2.0.33).
+
+******************************************************************************
+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 <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 "dmsdos.h"
+
+extern unsigned long dmsdos_speedup;
+
+#define MIN(x,y) ( ( (x)<(y) ) ? (x) : (y) )
+
+void do_cluster_reada(struct super_block*sb,int clusternr)
+{ /* read one cluster ahead without waiting for the data */
+ int nextclust;
+
+ nextclust=dbl_fat_nextcluster(sb,clusternr,NULL);
+ if(nextclust>0)
+ { /* no need to read-ahead if it is in cache */
+ /* for a simple search for existence we needn't lock the cache */
+ if(find_in_ccache(sb,nextclust,NULL,NULL)==NULL)
+ dmsdos_read_cluster(sb,NULL,nextclust);
+ }
+}
+
+int dblspace_file_read(
+ struct inode *inode,
+ struct file *filp,
+ char *buf,
+ int count)
+{
+ int clusternr;
+ /*unsigned char*clusterd;*/
+ int offset;
+ int bytes_read;
+ int membytes;
+ int membytes_bits;
+ int ret;
+ char * b;
+ int toread;
+ char datum;
+ int need_cluster;
+ Cluster_head*ch;
+ struct super_block*sb=inode->i_sb;
+ Dblsb*dblsb=MSDOS_SB(sb)->private_data;
+
+ if (!inode) {
+ printk(KERN_ERR "dmsdos_file_read: inode = NULL\n");
+ return -EINVAL;
+ }
+ /* S_ISLNK allows for UMSDOS. Should never happen for normal MSDOS */
+ if (!S_ISREG(inode->i_mode) && !S_ISLNK(inode->i_mode)) {
+ printk(KERN_ERR "dmsdos_file_read: mode = %07o\n",inode->i_mode);
+ return -EINVAL;
+ }
+
+ if(count<=0)return 0;
+
+ if(filp->f_pos>=inode->i_size)return 0;
+
+ if(filp->f_pos+count>inode->i_size)count=inode->i_size-filp->f_pos;
+ ret=verify_area(VERIFY_WRITE, buf, count);
+ if(ret<0)return ret;
+ ret=0;
+
+ membytes=SECTOR_SIZE*dblsb->s_sectperclust;
+ membytes_bits=SECTOR_BITS+dblsb->s_spc_bits;
+
+ /* calculate clusternr for cluster to read */
+ offset=filp->f_pos&(membytes-1);
+ clusternr=get_cluster(inode,filp->f_pos>>membytes_bits);
+ if(clusternr<=0)
+ { printk(KERN_ERR "DMSDOS: file_readx: FAT mismatches file size for ino=%ld\n",
+ inode->i_ino);
+ return 0;
+ }
+
+ bytes_read=0;
+ b=buf;
+
+ if(MSDOS_I(inode)->i_binary==0)goto text_read;
+
+ do
+ { ch=ch_read(sb,clusternr,0);
+ ret=(ch==NULL)?-EIO:0;
+ if(ret>=0)
+ { if(count>READA_THRESHOLD&&(dmsdos_speedup&SP_USE_READ_AHEAD)!=0)
+ do_cluster_reada(inode->i_sb,clusternr);
+ toread=(membytes-offset>count) ? count : membytes-offset;
+ /*printk(KERN_DEBUG "DMSDOS file_readx: memcpy_tofs(0x%08x,0x%08x,0x%08x)\n",
+ b,clusterd+offset,toread);*/
+ memcpy_tofs(b,ch->c_data+offset,toread);
+ bytes_read+=toread;
+ filp->f_pos+=toread;
+ count-=toread;
+ ch_free(ch);
+ if(count>0)
+ { b+=toread;
+ offset=0;
+ clusternr=get_cluster(inode,filp->f_pos>>membytes_bits);
+ if(filp->f_pos&(membytes-1))
+ panic("DMSDOS: read_file bug: f_pos not cluster-aligned");
+ if(clusternr<=0)
+ { ret=-EIO;
+ printk(KERN_ERR "DMSDOS: file_readx: FAT mismatches file size for ino=%ld\n",
+ inode->i_ino);
+ }
+ }
+ }
+ }
+ while(count>0&&ret>=0);
+
+ return (bytes_read==0&&ret<0)?ret:bytes_read;
+
+text_read:
+ /* ok, let's do it byte for byte..... */
+ bytes_read=0;
+ need_cluster=1;
+ ch=NULL;
+ while(count>0&&inode->i_size>filp->f_pos)
+ { if(need_cluster)
+ { clusternr=get_cluster(inode,filp->f_pos>>membytes_bits);
+ if(clusternr<=0)
+ { printk(KERN_ERR "DMSDOS: get_cluster failed (FAT problem ?)\n");
+ return -EIO;
+ }
+ ch=ch_read(sb,clusternr,0);
+ if(ch==NULL)return -EIO;
+ if(count>READA_THRESHOLD&&(dmsdos_speedup&SP_USE_READ_AHEAD)!=0)
+ do_cluster_reada(inode->i_sb,clusternr);
+ clusternr=dbl_fat_nextcluster(sb,clusternr,NULL);
+ need_cluster=0;
+ }
+
+ datum=ch->c_data[offset++];
+
+ ++(filp->f_pos);
+ if(datum!=13)
+ { put_fs_byte(datum,&(buf[bytes_read]));
+ ++bytes_read;
+ --count;
+ }
+ if(offset==membytes)
+ { need_cluster=1;
+ ch_free(ch);
+ ch=NULL;
+ offset=0;
+ }
+ }
+
+ if(ch)ch_free(ch);
+ return bytes_read;
+}
+
+int dblspace_file_write(struct inode *inode, struct file *filp,
+ const char *buf, int count)
+{ int cluster;
+ int ret=0;
+ unsigned int offset;
+ const unsigned char *b;
+ int canwrite;
+ int written;
+ int clustersize;
+ int clustersize_bits;
+ int uc;
+ int cr;
+ char datum;
+ int need_cluster;
+ Cluster_head*ch;
+ struct super_block*sb=inode->i_sb;
+ Dblsb*dblsb=MSDOS_SB(sb)->private_data;
+
+ if (!inode) {
+ printk(KERN_ERR "dmsdos_file_write: inode = NULL\n");
+ return -EINVAL;
+ }
+ /* S_ISLNK allows for UMSDOS. Should never happen for normal MSDOS */
+ if (!S_ISREG(inode->i_mode) && !S_ISLNK(inode->i_mode)) {
+ printk(KERN_ERR "dmsdos_file_write: mode = %07o\n",inode->i_mode);
+ return -EINVAL;
+ }
+/*
+ * ok, append may not work when many processes are writing at the same time
+ * but so what. That way leads to madness anyway.
+ */
+ if(sb->s_flags&MS_RDONLY)
+ { printk(KERN_ERR "DMSDOS: file_write: READ-ONLY filesystem\n");
+ return -EROFS;
+ }
+
+ if(dblsb->s_comp==READ_ONLY)return -EPERM;
+
+ if (filp->f_flags & O_APPEND) filp->f_pos = inode->i_size;
+ if (count <= 0) return 0;
+
+ ret=verify_area(VERIFY_READ, buf, count);
+ if(ret<0)return ret;
+ ret=0;
+
+ clustersize=dblsb->s_sectperclust*SECTOR_SIZE;
+ clustersize_bits=dblsb->s_spc_bits+SECTOR_BITS;
+
+ uc=0;
+ if(dmsdos_speedup&SP_NO_EMD_COMPR)
+ uc=(MSDOS_I(inode)->i_binary>1)?1:0; /* uncompressed forced */
+
+ offset=filp->f_pos&(clustersize-1);
+ do
+ { cluster=get_cluster(inode,filp->f_pos>>clustersize_bits);
+ if(cluster>0)break;
+ if(dblsb->s_full==2)
+ { printk(KERN_ERR "DMSDOS: write_file: CVF full (full flag set)\n");
+ return -ENOSPC;
+ }
+ if(dblsb->s_free_sectors<MIN_FREE_SECTORS)
+ { printk(KERN_ERR "DMSDOS: write_file: CVF full (free sector count too low)\n");
+ return -ENOSPC;
+ }
+ if(fat_add_cluster(inode)<0)
+ { printk(KERN_ERR "DMSDOS: write_file: fat_add_cluster failed\n");
+ return -ENOSPC;
+ }
+ }
+ while(cluster<=0);
+
+ LOG_CLUST("DMSDOS: file_write: beginning with cluster %d\n",
+ cluster);
+
+ b=buf;
+ written=0;
+
+ if(MSDOS_I(inode)->i_binary==0)goto text_write;
+
+ while(count>0)
+ {
+ if(offset>0||count<clustersize)
+ { /* cluster must be read because it will only partially overwritten */
+ LOG_CLUST("DMSDOS: write_file: reading cluster %d...\n",cluster);
+ ch=ch_read(sb,cluster,C_KEEP_LOCK);
+ if(ch==NULL)
+ { printk(KERN_ERR "DMSDOS: write_file: read_cluster failed!\n");
+ ret=-EIO;
+ break;
+ }
+ /*lock_ch(ch); we call with KEEP_LOCK above*/
+ }
+ else
+ { /* cluster will be fully overwritten, don't read it */
+ ch=ch_read(sb,cluster,C_KEEP_LOCK|C_NO_READ);
+ if(ch==NULL)
+ { printk(KERN_ERR "DMSDOS: write_file: ch_noread failed!\n");
+ ret=-EIO;
+ break;
+ }
+ /*lock_ch(ch); we call with KEEP_LOCK above */
+ ch->c_length= (count+offset<clustersize) ?
+ count+offset : clustersize;
+ }
+ canwrite=MIN(clustersize-offset,count);
+ memcpy_fromfs(&(ch->c_data[offset]),b,canwrite);
+
+ /* did cluster grow ? */
+ if(canwrite+offset>ch->c_length)
+ { /*printk(KERN_DEBUG "DMSDOS: write_file: write beyond logical cluster end, appending.\n");
+ */
+ ch->c_length=canwrite+offset;
+ }
+ if(ch->c_length>clustersize)
+ { printk(KERN_WARNING "DMSDOS: write_file: length>clustersize ??? bug !!!\n");
+ ch->c_length=clustersize;
+ }
+
+ /*unlock_ch(ch); no not here*/
+
+ LOG_CLUST("DMSDOS: write_file: writing cluster %d...\n",cluster);
+
+ /* ch_dirty_locked unlocks the cluster */
+ if(ch_dirty_locked(ch,0,uc)<0)
+ { printk(KERN_ERR "DMSDOS: write_file: ch_dirty failed!\n");
+ ch_free(ch);
+ ch=NULL;
+ ret=-EIO;
+ break;
+ }
+ ch_free(ch);
+ ch=NULL;
+
+ offset=0;
+ b+=canwrite;
+ filp->f_pos+=canwrite;
+ written+=canwrite;
+ count-=canwrite;
+
+ if(count==0)break; /* braucht keinen neuen cluster mehr*/
+
+ /* next cluster ? */
+ cluster=get_cluster(inode,filp->f_pos>>clustersize_bits);
+ if(cluster<=0)
+ { LOG_CLUST("DMSDOS: write_file: write_loop: allocating new cluster\n");
+ if(dblsb->s_full==2)
+ { printk(KERN_ERR "DMSDOS: write_file: CVF full (full flag set)\n");
+ ret=-ENOSPC;
+ break;
+ }
+ if(dblsb->s_free_sectors<MIN_FREE_SECTORS)
+ { printk(KERN_ERR "DMSDOS: write_file: CVF full (free sector count too low)\n");
+ ret=-ENOSPC;
+ break;
+ }
+ if(fat_add_cluster(inode)<0)
+ { printk(KERN_ERR "DMSDOS: write_file: fat_add_cluster failed\n");
+ ret=-ENOSPC;
+ break;
+ }
+ cluster=get_cluster(inode,filp->f_pos>>clustersize_bits);
+ if(cluster<=0)
+ { printk(KERN_ERR "DMSDOS: write_file: something's wrong, cannot happen\n");
+ ret=-EIO;
+ break;
+ }
+ }
+ }
+
+ if(filp->f_pos>inode->i_size)
+ { inode->i_size=filp->f_pos;
+ inode->i_dirt=1;
+ }
+
+ return (written==0)?ret:written;
+
+text_write:
+ written=0;
+ need_cluster=1;
+ cr=0;
+ ch=NULL;
+ while(count>0||cr==1)
+ { if(need_cluster)
+ { LOG_CLUST("DMSDOS: text_write: need_cluster=%d\n",
+ cluster);
+ /* we cannot simply calculate here...
+ so we never know and must always read the cluster */
+ ch=ch_read(sb,cluster,C_KEEP_LOCK);
+ if(ch==NULL)
+ { printk(KERN_ERR "DMSDOS: write_file: read_cluster failed!\n");
+ ret=-EIO;
+ break;
+ }
+ /*lock_ch(ch); we call with KEEP_LOCK above */
+ need_cluster=0;
+ }
+
+ if(cr==1)
+ { datum=10;
+ cr=0;
+ }
+ else
+ { datum=get_fs_byte(&(buf[written]));
+ ++written;
+ if(datum==10)
+ { datum=13;
+ cr=1;
+ }
+ --count;
+ }
+
+ ch->c_data[offset++]=datum;
+
+ ++(filp->f_pos);
+ if(offset>ch->c_length)ch->c_length=offset;
+
+ if(offset==clustersize)
+ { /* cluster is full and must be written back */
+ /*unlock_ch(ch);*/
+ /* ch_dirty_locked unlocks the cluster *after* write :) */
+ if(ch_dirty_locked(ch,0,uc)<0)
+ { printk(KERN_ERR "DMSDOS: write_file: ch_dirty failed!\n");
+ ch_free(ch);
+ ch=NULL;
+ ret=-EIO;
+ break;
+ }
+ ch_free(ch);
+ ch=NULL;
+
+ /*check whether end reached */
+ if(count==0&&cr==0)
+ { offset=0; /* tells that there's no rest */
+ break;
+ }
+ /* check whether a new cluster is needed */
+ cluster=get_cluster(inode,filp->f_pos>>clustersize_bits);
+ if(cluster<=0)
+ {
+ if(dblsb->s_full==2)
+ { printk(KERN_ERR "DMSDOS: write_file: CVF full (full flag set)\n");
+ ret=-ENOSPC;
+ break;
+ }
+ if(dblsb->s_free_sectors<MIN_FREE_SECTORS)
+ { printk(KERN_ERR "DMSDOS: write_file: CVF full (free sector count too low)\n");
+ ret=-ENOSPC;
+ break;
+ }
+ if(fat_add_cluster(inode)<0)
+ { printk(KERN_ERR "DMSDOS: write_file: fat_add_cluster failed\n");
+ ret=-ENOSPC;
+ break;
+ }
+ cluster=get_cluster(inode,filp->f_pos>>clustersize_bits);
+ if(cluster<=0)
+ { printk(KERN_ERR "DMSDOS: write_file: something wrong, cannot happen\n");
+ ret=-EIO;
+ break;
+ }
+ ch=ch_read(sb,cluster,C_KEEP_LOCK|C_NO_READ);
+ if(ch==NULL)
+ { printk(KERN_ERR "DMSDOS: write_file: ch_noread failed\n");
+ ret=-EIO;
+ break;
+ }
+ /*lock_ch(ch); we called with KEEP_LOCK above */
+ ch->c_length=SECTOR_SIZE;
+ }
+ else need_cluster=1;
+ offset=0;
+ }
+ }
+
+ /* check whether there's a rest to be written */
+ if(offset)
+ { /*unlock_ch(ch);*/
+ /* ch_dirty_locked unlocks the cluster *after* write */
+ if(ch_dirty_locked(ch,0,uc)<0)
+ { printk(KERN_ERR "DMSDOS: write_file: ch_dirty failed!\n");
+ if(ret==0)ret=-EIO;
+ }
+ }
+
+ if(ch)ch_free(ch);
+
+ if(filp->f_pos>inode->i_size)
+ { inode->i_size=filp->f_pos;
+ inode->i_dirt=1;
+ }
+
+ return (written==0)?ret:written;
+}
+
+#ifdef DMSDOS_USE_READPAGE
+/* Grmpf.... partially untested code. Don't know an application that does
+ writable mmaps, but it would be needed for testing this piece of code */
+
+/* idea: kernel does buffer reads with bmap calculated buffers - impossible
+ for dmsdos. (see kernel mmap code).
+ kernel emulates writable mmap by calling file_write when no buffers
+ are present - should work for dmsdos.
+ so we do file_read-emulated readable mmaps here though the
+ generic_mmap function is used.
+*/
+
+int read_the_page(unsigned long address, unsigned long pos,
+ struct inode*inode)
+{
+ unsigned int clear;
+ long gap; /* distance from eof to pos */
+
+ LOG_FS("DMSDOS: read_the_page\n");
+
+ address &= PAGE_MASK;
+
+ clear = 0;
+ gap = inode->i_size - pos;
+ if (gap <= 0){
+ /* mmaping beyond end of file */
+ clear = PAGE_SIZE;
+ }else{
+ int cur_read;
+ int need_read;
+ struct file filp;
+ if (gap < PAGE_SIZE){
+ clear = PAGE_SIZE - gap;
+ }
+ filp.f_reada = 0;
+ filp.f_pos = pos;
+ need_read = PAGE_SIZE - clear;
+ {
+ unsigned long cur_fs = get_fs();
+ set_fs (KERNEL_DS);
+ cur_read = dblspace_file_read (inode,&filp,
+ (char*)address,
+ need_read);
+ set_fs (cur_fs);
+ }
+ if (cur_read != need_read){
+ printk (KERN_ERR "DMSDOS: Error while reading an mmap file %d <> %d\n"
+ ,cur_read,need_read);
+ return -1;
+ }
+ }
+ if (clear > 0){
+ memset ((char*)address+PAGE_SIZE-clear,0,clear);
+ }
+ return 0;
+}
+
+int dblspace_readpage(struct inode *inode, struct page *page)
+{ unsigned long address;
+ int error = -1;
+
+ LOG_FS("DMSDOS: readpage %08lx\n", page_address(page));
+
+ address = page_address(page);
+ page->count++;
+ set_bit(PG_locked, &page->flags);
+
+ /* now read the data */
+ error=read_the_page(address,page->offset,inode);
+
+ if(error==0)set_bit(PG_uptodate, &page->flags);
+
+ clear_bit(PG_locked, &page->flags);
+ wake_up(&page->wait);
+
+ free_page(address);
+ return error;
+}
+
+#else /* DMSDOS_USE_READPAGE */
+
+/*
+ * Fill in the supplied page for mmap
+ */
+static unsigned long dblspace_file_mmap_nopage(
+ struct vm_area_struct * area,
+ unsigned long address,
+ int error_code)
+{
+ struct inode * inode = area->vm_inode;
+ unsigned long page;
+ unsigned int clear;
+ int pos;
+ long gap; /* distance from eof to pos */
+
+ LOG_FS("DMSDOS: file_mmap_nopage\n");
+ page = __get_free_page(GFP_KERNEL);
+ if (!page)return page;
+
+ address &= PAGE_MASK;
+ pos = address - area->vm_start + area->vm_offset;
+
+ clear = 0;
+ gap = inode->i_size - pos;
+ if (gap <= 0){
+ /* mmaping beyond end of file */
+ clear = PAGE_SIZE;
+ }else{
+ int cur_read;
+ int need_read;
+ struct file filp;
+ if (gap < PAGE_SIZE){
+ clear = PAGE_SIZE - gap;
+ }
+ filp.f_reada = 0;
+ filp.f_pos = pos;
+ need_read = PAGE_SIZE - clear;
+ {
+ unsigned long cur_fs = get_fs();
+ set_fs (KERNEL_DS);
+ cur_read = dblspace_file_read (inode,&filp,(char*)page
+ ,need_read);
+ set_fs (cur_fs);
+ }
+ if (cur_read != need_read){
+ printk (KERN_ERR "DMSDOS: Error while reading an mmap file %d <> %d\n"
+ ,cur_read,need_read);
+ }
+ }
+ if (clear > 0){
+ memset ((char*)page+PAGE_SIZE-clear,0,clear);
+ }
+ return page;
+}
+
+struct vm_operations_struct dblspace_file_mmap = {
+ NULL, /* open */
+ NULL, /* close */
+ NULL, /* unmap */
+ NULL, /* protect */
+ NULL, /* sync */
+ NULL, /* advise */
+ dblspace_file_mmap_nopage, /* nopage */
+ NULL, /* wppage */
+ NULL, /* swapout */
+ NULL, /* swapin */
+};
+
+/*
+ * This is used for a general mmap of a dmsdos file
+ * Returns 0 if ok, or a negative error code if not.
+ */
+int dblspace_mmap(struct inode*inode,struct file*file,struct vm_area_struct*vma)
+{
+ LOG_FS("DMSDOS: mmap ino=%ld\n",inode->i_ino);
+ if (vma->vm_flags & VM_SHARED) /* only PAGE_COW or read-only supported now */
+ return -EINVAL;
+ if (vma->vm_offset & (inode->i_sb->s_blocksize - 1))
+ return -EINVAL;
+ if (!inode->i_sb || !S_ISREG(inode->i_mode))
+ return -EACCES;
+ if (!IS_RDONLY(inode)) {
+ inode->i_atime = CURRENT_TIME;
+ inode->i_dirt = 1;
+ }
+
+ vma->vm_inode = inode;
+ inode->i_count++;
+ vma->vm_ops = &dblspace_file_mmap;
+ return 0;
+}
+#endif /* else / DMSDOS_USE_READPAGE */
+
diff --git a/src/dblspace_fileops.c-2.1.80 b/src/dblspace_fileops.c-2.1.80
new file mode 100644
index 0000000..554043c
--- /dev/null
+++ b/src/dblspace_fileops.c-2.1.80
@@ -0,0 +1,722 @@
+/*
+dblspace_fileops.c-2.1.80
+
+DMSDOS CVF-FAT module: file operation routines (for kernel>=2.1.80).
+
+******************************************************************************
+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 <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 "dmsdos.h"
+
+extern unsigned long dmsdos_speedup;
+
+#define MIN(x,y) ( ( (x)<(y) ) ? (x) : (y) )
+
+void do_cluster_reada(struct super_block*sb,int clusternr)
+{ /* read one cluster ahead without waiting for the data */
+ int nextclust;
+
+ nextclust=dbl_fat_nextcluster(sb,clusternr,NULL);
+ if(nextclust>0)
+ { /* no need to read-ahead if it is in cache */
+ /* for a simple search for existence we needn't lock the cache */
+ if(find_in_ccache(sb,nextclust,NULL,NULL)==NULL)
+ dmsdos_read_cluster(sb,NULL,nextclust);
+ }
+}
+
+int dblspace_file_read(
+ struct file *filp,
+ char *buf,
+ size_t count,
+ loff_t *ppos)
+{
+ int clusternr;
+ /*unsigned char*clusterd;*/
+ int offset;
+ int bytes_read;
+ int membytes;
+ int membytes_bits;
+ int ret;
+ char * b;
+ int toread;
+ char datum;
+ int need_cluster;
+ Cluster_head*ch;
+ struct inode *inode;
+ struct super_block*sb;
+ Dblsb*dblsb;
+
+ LOG_FS("DMSDOS: file_read start...\n");
+
+ inode = filp->f_dentry->d_inode;
+ LOG_FS("DMSDOS: file_read: got inode\n");
+ sb=inode->i_sb;
+ LOG_FS("DMSDOS: file_read: got sb\n");
+ dblsb=MSDOS_SB(sb)->private_data;
+ LOG_FS("DMSDOS: file_read: got dblsb\n");
+
+ if (!inode) {
+ printk(KERN_ERR "DMSDOS: file_read: inode = NULL, rejected.\n");
+ return -EINVAL;
+ }
+ /* S_ISLNK allows for UMSDOS. Should never happen for normal MSDOS */
+ if (!S_ISREG(inode->i_mode) && !S_ISLNK(inode->i_mode)) {
+ printk(KERN_ERR "DMSDOS: file_read: mode = %07o, rejected.\n",inode->i_mode);
+ return -EINVAL;
+ }
+
+ LOG_FS("DMSDOS: file_read init complete...\n");
+
+ if(count<=0)return 0;
+
+ if(*ppos>=inode->i_size)return 0;
+
+ if(*ppos+count>inode->i_size)count=inode->i_size-*ppos;
+
+ ret=verify_area(VERIFY_WRITE, buf, count);
+ if(ret<0)return ret;
+ ret=0;
+
+ membytes=SECTOR_SIZE*dblsb->s_sectperclust;
+ membytes_bits=SECTOR_BITS+dblsb->s_spc_bits;
+
+ /* calculate clusternr for cluster to read */
+ offset=*ppos&(membytes-1);
+ LOG_CLUST("DMSDOS: file_read: get_cluster...\n");
+ clusternr=get_cluster(inode,*ppos>>membytes_bits);
+ if(clusternr<=0)
+ { printk(KERN_ERR "DMSDOS: file_readx: FAT mismatches file size for ino=%ld\n",
+ inode->i_ino);
+ return 0;
+ }
+
+ bytes_read=0;
+ b=buf;
+
+ if(MSDOS_I(inode)->i_binary==0)goto text_read;
+
+ do
+ { LOG_CLUST("DMSDOS: file_read: calling ch_read...\n");
+ ch=ch_read(sb,clusternr,0);
+ LOG_CLUST("DMSDOS: file_read: after ch_read\n");
+ ret=(ch==NULL)?-EIO:0;
+ if(ret>=0)
+ { if(count>READA_THRESHOLD&&(dmsdos_speedup&SP_USE_READ_AHEAD)!=0)
+ do_cluster_reada(inode->i_sb,clusternr);
+ toread=(membytes-offset>count) ? count : membytes-offset;
+ /*printk("DMSDOS file_readx: memcpy_tofs(0x%08x,0x%08x,0x%08x)\n",
+ b,clusterd+offset,toread);*/
+ memcpy_tofs(b,ch->c_data+offset,toread);
+ bytes_read+=toread;
+ *ppos+=toread;
+ count-=toread;
+ ch_free(ch);
+ if(count>0)
+ { b+=toread;
+ offset=0;
+ LOG_CLUST("DMSDOS: file_read: get_cluster...\n");
+ clusternr=get_cluster(inode,*ppos>>membytes_bits);
+ if(*ppos&(membytes-1))
+ panic("DMSDOS: read_file bug: f_pos not cluster-aligned");
+ if(clusternr<=0)
+ { ret=-EIO;
+ printk(KERN_ERR "DMSDOS: file_readx: FAT mismatches file size for ino=%ld\n",
+ inode->i_ino);
+ }
+ }
+ }
+ }
+ while(count>0&&ret>=0);
+
+ return (bytes_read==0&&ret<0)?ret:bytes_read;
+
+text_read:
+ /* ok, let's do it byte for byte..... */
+ bytes_read=0;
+ need_cluster=1;
+ ch=NULL;
+ while(count>0&&inode->i_size>*ppos)
+ { if(need_cluster)
+ { clusternr=get_cluster(inode,*ppos>>membytes_bits);
+ if(clusternr<=0)
+ { printk(KERN_ERR "DMSDOS: get_cluster failed (FAT problem ?)\n");
+ return -EIO;
+ }
+ ch=ch_read(sb,clusternr,0);
+ if(ch==NULL)return -EIO;
+ if(count>READA_THRESHOLD&&(dmsdos_speedup&SP_USE_READ_AHEAD)!=0)
+ do_cluster_reada(inode->i_sb,clusternr);
+ clusternr=dbl_fat_nextcluster(sb,clusternr,NULL);
+ need_cluster=0;
+ }
+
+ datum=ch->c_data[offset++];
+
+ ++(*ppos);
+ if(datum!=13)
+ { /*put_fs_byte(datum,&(buf[bytes_read]));*/
+ memcpy_tofs(&(buf[bytes_read]),&datum,1);
+ ++bytes_read;
+ --count;
+ }
+ if(offset==membytes)
+ { need_cluster=1;
+ ch_free(ch);
+ ch=NULL;
+ offset=0;
+ }
+ }
+
+ if(ch)ch_free(ch);
+ return bytes_read;
+}
+
+int dblspace_file_write(
+ struct file *filp,
+ const char *buf,
+ size_t count,
+ loff_t *ppos)
+{
+ int cluster;
+ int ret=0;
+ unsigned int offset;
+ const unsigned char *b;
+ int canwrite;
+ int written;
+ int clustersize;
+ int clustersize_bits;
+ int uc;
+ int cr;
+ char datum;
+ int need_cluster;
+ Cluster_head*ch;
+ struct inode *inode = filp->f_dentry->d_inode;
+ struct super_block*sb=inode->i_sb;
+ Dblsb*dblsb=MSDOS_SB(sb)->private_data;
+
+ if (!inode) {
+ printk(KERN_ERR "dmsdos_file_write: inode = NULL\n");
+ return -EINVAL;
+ }
+ /* S_ISLNK allows for UMSDOS. Should never happen for normal MSDOS */
+ if (!S_ISREG(inode->i_mode) && !S_ISLNK(inode->i_mode)) {
+ printk(KERN_ERR "dmsdos_file_write: mode = %07o\n",inode->i_mode);
+ return -EINVAL;
+ }
+/*
+ * ok, append may not work when many processes are writing at the same time
+ * but so what. That way leads to madness anyway.
+ */
+ if(sb->s_flags&MS_RDONLY)
+ { printk(KERN_ERR "DMSDOS: file_write: READ-ONLY filesystem\n");
+ return -EROFS;
+ }
+
+ if(dblsb->s_comp==READ_ONLY)return -EPERM;
+
+ if (filp->f_flags & O_APPEND) *ppos = inode->i_size;
+ if (count <= 0) return 0;
+
+ ret=verify_area(VERIFY_READ, buf, count);
+ if(ret<0)return ret;
+ ret=0;
+
+ clustersize=dblsb->s_sectperclust*SECTOR_SIZE;
+ clustersize_bits=dblsb->s_spc_bits+SECTOR_BITS;
+
+ uc=0;
+ if(dmsdos_speedup&SP_NO_EMD_COMPR)
+ uc=(MSDOS_I(inode)->i_binary>1)?1:0; /* uncompressed forced */
+
+ offset=*ppos&(clustersize-1);
+ do
+ { cluster=get_cluster(inode,*ppos>>clustersize_bits);
+ if(cluster>0)break;
+ if(dblsb->s_full==2)
+ { printk(KERN_ERR "DMSDOS: write_file: CVF full (full flag set)\n");
+ return -ENOSPC;
+ }
+ if(dblsb->s_free_sectors<MIN_FREE_SECTORS)
+ { printk(KERN_ERR "DMSDOS: write_file: CVF full (free sector count too low)\n");
+ return -ENOSPC;
+ }
+ if(fat_add_cluster(inode)<0)
+ { printk(KERN_ERR "DMSDOS: write_file: fat_add_cluster failed\n");
+ return -ENOSPC;
+ }
+ }
+ while(cluster<=0);
+
+ LOG_CLUST("DMSDOS: file_write: beginning with cluster %d\n",
+ cluster);
+
+ b=buf;
+ written=0;
+
+ if(MSDOS_I(inode)->i_binary==0)goto text_write;
+
+ while(count>0)
+ {
+ if(offset>0||count<clustersize)
+ { /* cluster must be read because it will only partially overwritten */
+ LOG_CLUST("DMSDOS: write_file: reading cluster %d...\n",cluster);
+ ch=ch_read(sb,cluster,C_KEEP_LOCK);
+ if(ch==NULL)
+ { printk(KERN_ERR "DMSDOS: write_file: read_cluster failed!\n");
+ ret=-EIO;
+ break;
+ }
+ /*lock_ch(ch); we call with KEEP_LOCK above */
+ }
+ else
+ { /* cluster will be fully overwritten, don't read it */
+ ch=ch_read(sb,cluster,C_KEEP_LOCK|C_NO_READ);
+ if(ch==NULL)
+ { printk(KERN_ERR "DMSDOS: write_file: ch_noread failed!\n");
+ ret=-EIO;
+ break;
+ }
+ /*lock_ch(ch); we call with KEEP_LOCK above */
+ ch->c_length= (count+offset<clustersize) ?
+ count+offset : clustersize;
+ }
+ canwrite=MIN(clustersize-offset,count);
+ memcpy_fromfs(&(ch->c_data[offset]),b,canwrite);
+
+ /* did cluster grow ? */
+ if(canwrite+offset>ch->c_length)
+ { /*printk(KERN_ERR "DMSDOS: write_file: write beyond logical cluster end, appending.\n");
+ */
+ ch->c_length=canwrite+offset;
+ }
+ if(ch->c_length>clustersize)
+ { printk(KERN_ERR "DMSDOS: write_file: length>clustersize ??? bug !!!\n");
+ ch->c_length=clustersize;
+ }
+
+ /*unlock_ch(ch); no not here*/
+
+ LOG_CLUST("DMSDOS: write_file: writing cluster %d...\n",cluster);
+
+ /* ch_dirty_locked unlocks the cluster */
+ if(ch_dirty_locked(ch,0,uc)<0)
+ { printk(KERN_ERR "DMSDOS: write_file: ch_dirty failed!\n");
+ ch_free(ch);
+ ch=NULL;
+ ret=-EIO;
+ break;
+ }
+ ch_free(ch);
+ ch=NULL;
+
+ offset=0;
+ b+=canwrite;
+ *ppos+=canwrite;
+ written+=canwrite;
+ count-=canwrite;
+
+ if(count==0)break; /* braucht keinen neuen cluster mehr*/
+
+ /* next cluster ? */
+ cluster=get_cluster(inode,*ppos>>clustersize_bits);
+ if(cluster<=0)
+ { LOG_CLUST("DMSDOS: write_file: write_loop: allocating new cluster\n");
+ if(dblsb->s_full==2)
+ { printk(KERN_ERR "DMSDOS: write_file: CVF full (full flag set)\n");
+ ret=-ENOSPC;
+ break;
+ }
+ if(dblsb->s_free_sectors<MIN_FREE_SECTORS)
+ { printk(KERN_ERR "DMSDOS: write_file: CVF full (free sector count too low)\n");
+ ret=-ENOSPC;
+ break;
+ }
+ if(fat_add_cluster(inode)<0)
+ { printk(KERN_ERR "DMSDOS: write_file: fat_add_cluster failed\n");
+ ret=-ENOSPC;
+ break;
+ }
+ cluster=get_cluster(inode,*ppos>>clustersize_bits);
+ if(cluster<=0)
+ { printk(KERN_ERR "DMSDOS: write_file: something's wrong, cannot happen\n");
+ ret=-EIO;
+ break;
+ }
+ }
+ }
+
+ if(*ppos>inode->i_size)
+ { inode->i_size=*ppos;
+ /*inode->i_dirt=1; .... HMMM .... */
+ mark_inode_dirty(inode);
+ }
+
+ return (written==0)?ret:written;
+
+text_write:
+ written=0;
+ need_cluster=1;
+ cr=0;
+ ch=NULL;
+ while(count>0||cr==1)
+ { if(need_cluster)
+ { LOG_CLUST("DMSDOS: text_write: need_cluster=%d\n",
+ cluster);
+ /* we cannot simply calculate here...
+ so we never know and must always read the cluster */
+ ch=ch_read(sb,cluster,C_KEEP_LOCK);
+ if(ch==NULL)
+ { printk(KERN_ERR "DMSDOS: write_file: read_cluster failed!\n");
+ ret=-EIO;
+ break;
+ }
+ /*lock_ch(ch); we call with KEEP_LOCK above */
+ need_cluster=0;
+ }
+
+ if(cr==1)
+ { datum=10;
+ cr=0;
+ }
+ else
+ { /*datum=get_fs_byte(&(buf[written]));*/
+ memcpy_fromfs(&datum,&(buf[written]),1);
+ ++written;
+ if(datum==10)
+ { datum=13;
+ cr=1;
+ }
+ --count;
+ }
+
+ ch->c_data[offset++]=datum;
+
+ ++(*ppos);
+ if(offset>ch->c_length)ch->c_length=offset;
+
+ if(offset==clustersize)
+ { /* cluster is full and must be written back */
+ /*unlock_ch(ch);*/
+ /* ch_dirty_locked unlocks the cluster *after* write */
+ if(ch_dirty_locked(ch,0,uc)<0)
+ { printk(KERN_ERR "DMSDOS: write_file: ch_dirty failed!\n");
+ ch_free(ch);
+ ch=NULL;
+ ret=-EIO;
+ break;
+ }
+ ch_free(ch);
+ ch=NULL;
+
+ /*check whether end reached */
+ if(count==0&&cr==0)
+ { offset=0; /* tells that there's no rest */
+ break;
+ }
+ /* check whether a new cluster is needed */
+ cluster=get_cluster(inode,*ppos>>clustersize_bits);
+ if(cluster<=0)
+ {
+ if(dblsb->s_full==2)
+ { printk(KERN_ERR "DMSDOS: write_file: CVF full (full flag set)\n");
+ ret=-ENOSPC;
+ break;
+ }
+ if(dblsb->s_free_sectors<MIN_FREE_SECTORS)
+ { printk(KERN_ERR "DMSDOS: write_file: CVF full (free sector count too low)\n");
+ ret=-ENOSPC;
+ break;
+ }
+ if(fat_add_cluster(inode)<0)
+ { printk(KERN_ERR "DMSDOS: write_file: fat_add_cluster failed\n");
+ ret=-ENOSPC;
+ break;
+ }
+ cluster=get_cluster(inode,*ppos>>clustersize_bits);
+ if(cluster<=0)
+ { printk(KERN_ERR "DMSDOS: write_file: something wrong, cannot happen\n");
+ ret=-EIO;
+ break;
+ }
+ ch=ch_read(sb,cluster,C_KEEP_LOCK|C_NO_READ);
+ if(ch==NULL)
+ { printk(KERN_ERR "DMSDOS: write_file: ch_noread failed\n");
+ ret=-EIO;
+ break;
+ }
+ /*lock_ch(ch); we called with KEEP_LOCK above */
+ ch->c_length=SECTOR_SIZE;
+ }
+ else need_cluster=1;
+ offset=0;
+ }
+ }
+
+ /* check whether there's a rest to be written */
+ if(offset)
+ { /*unlock_ch(ch);*/
+ /* ch_dirty_locked unlocks the cluster *after* write */
+ if(ch_dirty_locked(ch,0,uc)<0)
+ { printk(KERN_ERR "DMSDOS: write_file: ch_dirty failed!\n");
+ if(ret==0)ret=-EIO;
+ }
+ }
+
+ if(ch)ch_free(ch);
+
+ if(*ppos>inode->i_size)
+ { inode->i_size=*ppos;
+ /*inode->i_dirt=1; .... HMMM .... */
+ mark_inode_dirty(inode);
+ }
+
+ return (written==0)?ret:written;
+}
+
+
+/* Grmpf.... partially untested code. Don't know an application that does
+ writable mmaps, but it would be needed for testing this piece of code */
+
+/* idea: kernel does buffer reads with bmap calculated buffers - impossible
+ for dmsdos. (see kernel mmap code).
+ kernel emulates writable mmap by calling file_write when no buffers
+ are present - should work for dmsdos.
+ so we do file_read-emulated readable mmaps here though the
+ generic_mmap function is used.
+*/
+
+#ifdef DMSDOS_USE_READPAGE
+int read_the_page(unsigned long address, unsigned long pos,
+ struct inode*inode)
+{
+ unsigned int clear;
+ long gap; /* distance from eof to pos */
+
+ LOG_FS("DMSDOS: read_the_page\n");
+
+ address &= PAGE_MASK;
+
+ clear = 0;
+ gap = inode->i_size - pos;
+ if (gap <= 0){
+ /* mmaping beyond end of file */
+ clear = PAGE_SIZE;
+ }else{
+ int cur_read;
+ int need_read;
+ struct file filp;
+ if (gap < PAGE_SIZE){
+ clear = PAGE_SIZE - gap;
+ }
+ filp.f_reada = 0;
+ filp.f_pos = pos;
+ need_read = PAGE_SIZE - clear;
+ { mm_segment_t cur_fs = get_fs();
+ filp.f_dentry=kmalloc(sizeof(struct dentry),GFP_KERNEL);
+ if(filp.f_dentry==NULL)
+ { printk(KERN_ERR "DMSDOS: read_the_page: no memory!\n");
+ return -1;
+ }
+ filp.f_dentry->d_inode=inode;
+
+ set_fs (KERNEL_DS);
+ LOG_FS("DMSDOS: read_the_page: calling file_read...\n");
+ cur_read = dblspace_file_read (&filp,
+ (char*)address,
+ need_read,
+ &(filp.f_pos));
+ set_fs (cur_fs);
+ kfree(filp.f_dentry);
+ }
+ if (cur_read != need_read){
+ printk ("DMSDOS: Error while reading an mmap file %d <> %d\n"
+ ,cur_read,need_read);
+ return -1;
+ }
+ }
+ if (clear > 0){
+ memset ((char*)address+PAGE_SIZE-clear,0,clear);
+ }
+ return 0;
+}
+
+#ifdef READPAGE_DENTRY
+int dblspace_readpage(struct dentry*dentry, struct page *page)
+{ unsigned long address;
+ int error = -1;
+ struct inode*inode=dentry->d_inode;
+#else
+int dblspace_readpage(struct inode *inode, struct page *page)
+{ unsigned long address;
+ int error = -1;
+#endif
+ LOG_FS("DMSDOS: readpage %08lx\n", page_address(page));
+
+ address = page_address(page);
+ atomic_inc(&page->count);
+ set_bit(PG_locked, &page->flags);
+
+ /* now read the data */
+ error=read_the_page(address,page->offset,inode);
+
+ LOG_FS("DMSDOS: readpage: read_the_page returned %d\n",error);
+
+ if(error==0)set_bit(PG_uptodate, &page->flags);
+
+ clear_bit(PG_locked, &page->flags);
+ wake_up(&page->wait);
+
+ free_page(address);
+ return error;
+}
+
+#else /* from: ifdef DMSDOS_USE_READPAGE */
+
+/* this is supposed to be obsolete stuff for older kernels... */
+
+#if LINUX_VERSION_CODE >= LVC(2,1,90)
+#error kernel >=2.1.90 requires readpage interface, rerun dmsdos configuration
+#endif
+
+/*
+ * Fill in the supplied page for mmap
+ */
+static unsigned long dblspace_file_mmap_nopage(
+ struct vm_area_struct * area,
+ unsigned long address,
+ int error_code)
+{
+ struct inode * inode = area->vm_dentry->d_inode;
+ unsigned long page;
+ unsigned int clear;
+ int pos;
+ long gap; /* distance from eof to pos */
+
+ LOG_FS("DMSDOS: file_mmap_nopage\n");
+ page = __get_free_page(GFP_KERNEL);
+ if (!page)return page;
+ LOG_FS("DMSDOS: file_mmap_nopage: got page\n");
+
+ address &= PAGE_MASK;
+ pos = address - area->vm_start + area->vm_offset;
+
+ clear = 0;
+ gap = inode->i_size - pos;
+ if (gap <= 0){
+ /* mmaping beyond end of file */
+ clear = PAGE_SIZE;
+ }else{
+ int cur_read;
+ int need_read;
+ struct file filp;
+ if (gap < PAGE_SIZE){
+ clear = PAGE_SIZE - gap;
+ }
+ filp.f_reada = 0;
+ filp.f_pos = pos;
+ need_read = PAGE_SIZE - clear;
+ { mm_segment_t cur_fs = get_fs();
+ filp.f_dentry=kmalloc(sizeof(struct dentry),GFP_KERNEL);
+ if(filp.f_dentry==NULL)
+ { printk(KERN_ERR "DMSDOS: file_mmap_nopage: no memory!\n");
+ return -1;
+ }
+ filp.f_dentry->d_inode=inode;
+
+ set_fs (KERNEL_DS);
+ LOG_FS("DMSDOS: file_mmap_nopage: calling file_read...\n");
+ cur_read = dblspace_file_read (&filp,(char*)page,
+ need_read,&filp.f_pos);
+ LOG_FS("DMSDOS: file_mmap_nopage: file_read returned\n");
+ set_fs (cur_fs);
+ kfree(filp.f_dentry);
+ }
+ if (cur_read != need_read){
+ printk ("DMSDOS: Error while reading an mmap file %d <> %d\n"
+ ,cur_read,need_read);
+ }
+ }
+ if (clear > 0){
+ memset ((char*)page+PAGE_SIZE-clear,0,clear);
+ }
+ LOG_FS("DMSDOS: file_mmap_nopage: end\n");
+ return page;
+}
+
+struct vm_operations_struct dblspace_file_mmap = {
+ NULL, /* open */
+ NULL, /* close */
+ NULL, /* unmap */
+ NULL, /* protect */
+ NULL, /* sync */
+ NULL, /* advise */
+ dblspace_file_mmap_nopage, /* nopage */
+ NULL, /* wppage */
+ NULL, /* swapout */
+ NULL, /* swapin */
+};
+
+/*
+ * This is used for a general mmap of a dmsdos file
+ * Returns 0 if ok, or a negative error code if not.
+ */
+int dblspace_mmap(struct file*file,struct vm_area_struct*vma)
+{ struct inode *inode = file->f_dentry->d_inode;
+ LOG_FS("DMSDOS: mmap ino=%ld\n",inode->i_ino);
+ if (vma->vm_flags & VM_SHARED) /* only PAGE_COW or read-only supported now */
+ return -EINVAL;
+ if (vma->vm_offset & (inode->i_sb->s_blocksize - 1))
+ return -EINVAL;
+ if (!inode->i_sb || !S_ISREG(inode->i_mode))
+ return -EACCES;
+ if (!IS_RDONLY(inode)) {
+ inode->i_atime = CURRENT_TIME;
+ /*inode->i_dirt = 1;*/
+ mark_inode_dirty(inode);
+ }
+
+ vma->vm_dentry = dget(file->f_dentry);
+ vma->vm_ops = &dblspace_file_mmap;
+ return 0;
+}
+
+#endif /* from: else / ifdef DMSDOS_USE_READPAGE */
+
diff --git a/src/dblspace_fileops.c-2.3.10 b/src/dblspace_fileops.c-2.3.10
new file mode 100644
index 0000000..a5b9b51
--- /dev/null
+++ b/src/dblspace_fileops.c-2.3.10
@@ -0,0 +1,563 @@
+/*
+dblspace_fileops.c-2.1.80
+
+DMSDOS CVF-FAT module: file operation routines (for kernel>=2.1.80).
+
+******************************************************************************
+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 <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 "dmsdos.h"
+
+extern unsigned long dmsdos_speedup;
+
+#define MIN(x,y) ( ( (x)<(y) ) ? (x) : (y) )
+
+void do_cluster_reada(struct super_block*sb,int clusternr)
+{ /* read one cluster ahead without waiting for the data */
+ int nextclust;
+
+ nextclust=dbl_fat_nextcluster(sb,clusternr,NULL);
+ if(nextclust>0)
+ { /* no need to read-ahead if it is in cache */
+ /* for a simple search for existence we needn't lock the cache */
+ if(find_in_ccache(sb,nextclust,NULL,NULL)==NULL)
+ dmsdos_read_cluster(sb,NULL,nextclust);
+ }
+}
+
+int dblspace_file_read(
+ struct file *filp,
+ char *buf,
+ size_t count,
+ loff_t *ppos)
+{
+ int clusternr;
+ /*unsigned char*clusterd;*/
+ int offset;
+ int bytes_read;
+ int membytes;
+ int membytes_bits;
+ int ret;
+ char * b;
+ int toread;
+ Cluster_head*ch;
+ struct inode *inode;
+ struct super_block*sb;
+ Dblsb*dblsb;
+
+ LOG_FS("DMSDOS: file_read start...\n");
+
+ inode = filp->f_dentry->d_inode;
+ LOG_FS("DMSDOS: file_read: got inode\n");
+ sb=inode->i_sb;
+ LOG_FS("DMSDOS: file_read: got sb\n");
+ dblsb=MSDOS_SB(sb)->private_data;
+ LOG_FS("DMSDOS: file_read: got dblsb\n");
+
+ if (!inode) {
+ printk(KERN_ERR "DMSDOS: file_read: inode = NULL, rejected.\n");
+ return -EINVAL;
+ }
+ /* S_ISLNK allows for UMSDOS. Should never happen for normal MSDOS */
+ if (!S_ISREG(inode->i_mode) && !S_ISLNK(inode->i_mode)) {
+ printk(KERN_ERR "DMSDOS: file_read: mode = %07o, rejected.\n",inode->i_mode);
+ return -EINVAL;
+ }
+
+ LOG_FS("DMSDOS: file_read init complete...\n");
+
+ if(count<=0)return 0;
+
+ if(*ppos>=inode->i_size)return 0;
+
+ if(*ppos+count>inode->i_size)count=inode->i_size-*ppos;
+
+ ret=verify_area(VERIFY_WRITE, buf, count);
+ if(ret<0)return ret;
+ ret=0;
+
+ membytes=SECTOR_SIZE*dblsb->s_sectperclust;
+ membytes_bits=SECTOR_BITS+dblsb->s_spc_bits;
+
+ /* calculate clusternr for cluster to read */
+ offset=*ppos&(membytes-1);
+ LOG_CLUST("DMSDOS: file_read: get_cluster...\n");
+ clusternr=get_cluster(inode,*ppos>>membytes_bits);
+ if(clusternr<=0)
+ { printk(KERN_ERR "DMSDOS: file_readx: FAT mismatches file size for ino=%ld\n",
+ inode->i_ino);
+ return 0;
+ }
+
+ bytes_read=0;
+ b=buf;
+
+ do
+ { LOG_CLUST("DMSDOS: file_read: calling ch_read...\n");
+ ch=ch_read(sb,clusternr,0);
+ LOG_CLUST("DMSDOS: file_read: after ch_read\n");
+ ret=(ch==NULL)?-EIO:0;
+ if(ret>=0)
+ { if(count>READA_THRESHOLD&&(dmsdos_speedup&SP_USE_READ_AHEAD)!=0)
+ do_cluster_reada(inode->i_sb,clusternr);
+ toread=(membytes-offset>count) ? count : membytes-offset;
+ /*printk("DMSDOS file_readx: memcpy_tofs(0x%08x,0x%08x,0x%08x)\n",
+ b,clusterd+offset,toread);*/
+ memcpy_tofs(b,ch->c_data+offset,toread);
+ bytes_read+=toread;
+ *ppos+=toread;
+ count-=toread;
+ ch_free(ch);
+ if(count>0)
+ { b+=toread;
+ offset=0;
+ LOG_CLUST("DMSDOS: file_read: get_cluster...\n");
+ clusternr=get_cluster(inode,*ppos>>membytes_bits);
+ if(*ppos&(membytes-1))
+ panic("DMSDOS: read_file bug: f_pos not cluster-aligned");
+ if(clusternr<=0)
+ { ret=-EIO;
+ printk(KERN_ERR "DMSDOS: file_readx: FAT mismatches file size for ino=%ld\n",
+ inode->i_ino);
+ }
+ }
+ }
+ }
+ while(count>0&&ret>=0);
+
+ return (bytes_read==0&&ret<0)?ret:bytes_read;
+}
+
+int dblspace_file_write(
+ struct file *filp,
+ const char *buf,
+ size_t count,
+ loff_t *ppos)
+{
+ int cluster;
+ int ret=0;
+ unsigned int offset;
+ const unsigned char *b;
+ int canwrite;
+ int written;
+ int clustersize;
+ int clustersize_bits;
+ int uc;
+ Cluster_head*ch;
+ struct inode *inode = filp->f_dentry->d_inode;
+ struct super_block*sb=inode->i_sb;
+ Dblsb*dblsb=MSDOS_SB(sb)->private_data;
+
+ if (!inode) {
+ printk(KERN_ERR "dmsdos_file_write: inode = NULL\n");
+ return -EINVAL;
+ }
+ /* S_ISLNK allows for UMSDOS. Should never happen for normal MSDOS */
+ if (!S_ISREG(inode->i_mode) && !S_ISLNK(inode->i_mode)) {
+ printk(KERN_ERR "dmsdos_file_write: mode = %07o\n",inode->i_mode);
+ return -EINVAL;
+ }
+/*
+ * ok, append may not work when many processes are writing at the same time
+ * but so what. That way leads to madness anyway.
+ */
+ if(sb->s_flags&MS_RDONLY)
+ { printk(KERN_ERR "DMSDOS: file_write: READ-ONLY filesystem\n");
+ return -EROFS;
+ }
+
+ if(dblsb->s_comp==READ_ONLY)return -EPERM;
+
+ if (filp->f_flags & O_APPEND) *ppos = inode->i_size;
+ if (count <= 0) return 0;
+
+ ret=verify_area(VERIFY_READ, buf, count);
+ if(ret<0)return ret;
+ ret=0;
+
+ clustersize=dblsb->s_sectperclust*SECTOR_SIZE;
+ clustersize_bits=dblsb->s_spc_bits+SECTOR_BITS;
+
+ uc=0;
+ #if 0
+ if(dmsdos_speedup&SP_NO_EMD_COMPR)
+ uc=(MSDOS_I(inode)->i_binary>1)?1:0; /* uncompressed forced */
+ #endif
+ #warning Umsdos EMD speedup is not implemented
+
+ offset=*ppos&(clustersize-1);
+ do
+ { cluster=get_cluster(inode,*ppos>>clustersize_bits);
+ if(cluster>0)break;
+ if(dblsb->s_full==2)
+ { printk(KERN_ERR "DMSDOS: write_file: CVF full (full flag set)\n");
+ return -ENOSPC;
+ }
+ if(dblsb->s_free_sectors<MIN_FREE_SECTORS)
+ { printk(KERN_ERR "DMSDOS: write_file: CVF full (free sector count too low)\n");
+ return -ENOSPC;
+ }
+ if(fat_add_cluster(inode)<0)
+ { printk(KERN_ERR "DMSDOS: write_file: fat_add_cluster failed\n");
+ return -ENOSPC;
+ }
+ }
+ while(cluster<=0);
+
+ LOG_CLUST("DMSDOS: file_write: beginning with cluster %d\n",
+ cluster);
+
+ b=buf;
+ written=0;
+
+ while(count>0)
+ {
+ if(offset>0||count<clustersize)
+ { /* cluster must be read because it will only partially overwritten */
+ LOG_CLUST("DMSDOS: write_file: reading cluster %d...\n",cluster);
+ ch=ch_read(sb,cluster,C_KEEP_LOCK);
+ if(ch==NULL)
+ { printk(KERN_ERR "DMSDOS: write_file: read_cluster failed!\n");
+ ret=-EIO;
+ break;
+ }
+ /*lock_ch(ch); we call with KEEP_LOCK above */
+ }
+ else
+ { /* cluster will be fully overwritten, don't read it */
+ ch=ch_read(sb,cluster,C_KEEP_LOCK|C_NO_READ);
+ if(ch==NULL)
+ { printk(KERN_ERR "DMSDOS: write_file: ch_noread failed!\n");
+ ret=-EIO;
+ break;
+ }
+ /*lock_ch(ch); we call with KEEP_LOCK above */
+ ch->c_length= (count+offset<clustersize) ?
+ count+offset : clustersize;
+ }
+ canwrite=MIN(clustersize-offset,count);
+ memcpy_fromfs(&(ch->c_data[offset]),b,canwrite);
+
+ /* did cluster grow ? */
+ if(canwrite+offset>ch->c_length)
+ { /*printk(KERN_ERR "DMSDOS: write_file: write beyond logical cluster end, appending.\n");
+ */
+ ch->c_length=canwrite+offset;
+ }
+ if(ch->c_length>clustersize)
+ { printk(KERN_ERR "DMSDOS: write_file: length>clustersize ??? bug !!!\n");
+ ch->c_length=clustersize;
+ }
+
+ /*unlock_ch(ch); no not here*/
+
+ LOG_CLUST("DMSDOS: write_file: writing cluster %d...\n",cluster);
+
+ /* ch_dirty_locked unlocks the cluster */
+ if(ch_dirty_locked(ch,0,uc)<0)
+ { printk(KERN_ERR "DMSDOS: write_file: ch_dirty failed!\n");
+ ch_free(ch);
+ ch=NULL;
+ ret=-EIO;
+ break;
+ }
+ ch_free(ch);
+ ch=NULL;
+
+ offset=0;
+ b+=canwrite;
+ *ppos+=canwrite;
+ written+=canwrite;
+ count-=canwrite;
+
+ if(count==0)break; /* braucht keinen neuen cluster mehr*/
+
+ /* next cluster ? */
+ cluster=get_cluster(inode,*ppos>>clustersize_bits);
+ if(cluster<=0)
+ { LOG_CLUST("DMSDOS: write_file: write_loop: allocating new cluster\n");
+ if(dblsb->s_full==2)
+ { printk(KERN_ERR "DMSDOS: write_file: CVF full (full flag set)\n");
+ ret=-ENOSPC;
+ break;
+ }
+ if(dblsb->s_free_sectors<MIN_FREE_SECTORS)
+ { printk(KERN_ERR "DMSDOS: write_file: CVF full (free sector count too low)\n");
+ ret=-ENOSPC;
+ break;
+ }
+ if(fat_add_cluster(inode)<0)
+ { printk(KERN_ERR "DMSDOS: write_file: fat_add_cluster failed\n");
+ ret=-ENOSPC;
+ break;
+ }
+ cluster=get_cluster(inode,*ppos>>clustersize_bits);
+ if(cluster<=0)
+ { printk(KERN_ERR "DMSDOS: write_file: something's wrong, cannot happen\n");
+ ret=-EIO;
+ break;
+ }
+ }
+ }
+
+ if(*ppos>inode->i_size)
+ { inode->i_size=*ppos;
+ /*inode->i_dirt=1; .... HMMM .... */
+ mark_inode_dirty(inode);
+ }
+
+ return (written==0)?ret:written;
+}
+
+
+/* Grmpf.... partially untested code. Don't know an application that does
+ writable mmaps, but it would be needed for testing this piece of code */
+
+/* idea: kernel does buffer reads with bmap calculated buffers - impossible
+ for dmsdos. (see kernel mmap code).
+ kernel emulates writable mmap by calling file_write when no buffers
+ are present - should work for dmsdos.
+ so we do file_read-emulated readable mmaps here though the
+ generic_mmap function is used.
+*/
+
+#ifdef DMSDOS_USE_READPAGE
+int read_the_page(unsigned long address, unsigned long pos,
+ struct inode*inode)
+{
+ unsigned int clear;
+ long gap; /* distance from eof to pos */
+
+ LOG_FS("DMSDOS: read_the_page\n");
+
+ address &= PAGE_MASK;
+
+ clear = 0;
+ gap = inode->i_size - pos;
+ if (gap <= 0){
+ /* mmaping beyond end of file */
+ clear = PAGE_SIZE;
+ }else{
+ int cur_read;
+ int need_read;
+ struct file filp;
+ if (gap < PAGE_SIZE){
+ clear = PAGE_SIZE - gap;
+ }
+ filp.f_reada = 0;
+ filp.f_pos = pos;
+ need_read = PAGE_SIZE - clear;
+ { mm_segment_t cur_fs = get_fs();
+ filp.f_dentry=kmalloc(sizeof(struct dentry),GFP_KERNEL);
+ if(filp.f_dentry==NULL)
+ { printk(KERN_ERR "DMSDOS: read_the_page: no memory!\n");
+ return -1;
+ }
+ filp.f_dentry->d_inode=inode;
+
+ set_fs (KERNEL_DS);
+ LOG_FS("DMSDOS: read_the_page: calling file_read...\n");
+ cur_read = dblspace_file_read (&filp,
+ (char*)address,
+ need_read,
+ &(filp.f_pos));
+ set_fs (cur_fs);
+ kfree(filp.f_dentry);
+ }
+ if (cur_read != need_read){
+ printk ("DMSDOS: Error while reading an mmap file %d <> %d\n"
+ ,cur_read,need_read);
+ return -1;
+ }
+ }
+ if (clear > 0){
+ memset ((char*)address+PAGE_SIZE-clear,0,clear);
+ }
+ return 0;
+}
+
+#ifndef READPAGE_DENTRY
+int dblspace_readpage(struct file *file, struct page *page)
+#else
+int dblspace_readpage(struct dentry*dentry, struct page *page)
+#endif
+{ unsigned long address;
+ int error = -1;
+ #ifndef READPAGE_DENTRY
+ struct dentry * dentry = file->f_dentry;
+ #endif
+ struct inode * inode = dentry->d_inode;
+
+ LOG_FS("DMSDOS: readpage %08lx\n", page_address(page));
+
+ address = page_address(page);
+ atomic_inc(&page->count);
+ set_bit(PG_locked, &page->flags);
+
+ /* now read the data */
+#ifndef __FOR_KERNEL_2_3_30
+ error=read_the_page(address,page->offset,inode);
+#else /* __FOR_KERNEL_2_3_30 */
+ error=read_the_page(address,page->index<<PAGE_CACHE_SHIFT,inode);
+#endif /* __FOR_KERNEL_2_3_30 */
+
+ LOG_FS("DMSDOS: readpage: read_the_page returned %d\n",error);
+
+ if(error==0)set_bit(PG_uptodate, &page->flags);
+
+ clear_bit(PG_locked, &page->flags);
+ wake_up(&page->wait);
+
+ free_page(address);
+ return error;
+}
+
+#else /* from: ifdef DMSDOS_USE_READPAGE */
+
+/* this is supposed to be obsolete stuff for older kernels... */
+
+#if LINUX_VERSION_CODE >= LVC(2,1,90)
+#error kernel >=2.1.90 requires readpage interface, rerun dmsdos configuration
+#endif
+
+/*
+ * Fill in the supplied page for mmap
+ */
+static unsigned long dblspace_file_mmap_nopage(
+ struct vm_area_struct * area,
+ unsigned long address,
+ int error_code)
+{
+ struct inode * inode = area->vm_dentry->d_inode;
+ unsigned long page;
+ unsigned int clear;
+ int pos;
+ long gap; /* distance from eof to pos */
+
+ LOG_FS("DMSDOS: file_mmap_nopage\n");
+ page = __get_free_page(GFP_KERNEL);
+ if (!page)return page;
+ LOG_FS("DMSDOS: file_mmap_nopage: got page\n");
+
+ address &= PAGE_MASK;
+ pos = address - area->vm_start + area->vm_offset;
+
+ clear = 0;
+ gap = inode->i_size - pos;
+ if (gap <= 0){
+ /* mmaping beyond end of file */
+ clear = PAGE_SIZE;
+ }else{
+ int cur_read;
+ int need_read;
+ struct file filp;
+ if (gap < PAGE_SIZE){
+ clear = PAGE_SIZE - gap;
+ }
+ filp.f_reada = 0;
+ filp.f_pos = pos;
+ need_read = PAGE_SIZE - clear;
+ { mm_segment_t cur_fs = get_fs();
+ filp.f_dentry=kmalloc(sizeof(struct dentry),GFP_KERNEL);
+ if(filp.f_dentry==NULL)
+ { printk(KERN_ERR "DMSDOS: file_mmap_nopage: no memory!\n");
+ return -1;
+ }
+ filp.f_dentry->d_inode=inode;
+
+ set_fs (KERNEL_DS);
+ LOG_FS("DMSDOS: file_mmap_nopage: calling file_read...\n");
+ cur_read = dblspace_file_read (&filp,(char*)page,
+ need_read,&filp.f_pos);
+ LOG_FS("DMSDOS: file_mmap_nopage: file_read returned\n");
+ set_fs (cur_fs);
+ kfree(filp.f_dentry);
+ }
+ if (cur_read != need_read){
+ printk ("DMSDOS: Error while reading an mmap file %d <> %d\n"
+ ,cur_read,need_read);
+ }
+ }
+ if (clear > 0){
+ memset ((char*)page+PAGE_SIZE-clear,0,clear);
+ }
+ LOG_FS("DMSDOS: file_mmap_nopage: end\n");
+ return page;
+}
+
+struct vm_operations_struct dblspace_file_mmap = {
+ NULL, /* open */
+ NULL, /* close */
+ NULL, /* unmap */
+ NULL, /* protect */
+ NULL, /* sync */
+ NULL, /* advise */
+ dblspace_file_mmap_nopage, /* nopage */
+ NULL, /* wppage */
+ NULL, /* swapout */
+ NULL, /* swapin */
+};
+
+/*
+ * This is used for a general mmap of a dmsdos file
+ * Returns 0 if ok, or a negative error code if not.
+ */
+int dblspace_mmap(struct file*file,struct vm_area_struct*vma)
+{ struct inode *inode = file->f_dentry->d_inode;
+ LOG_FS("DMSDOS: mmap ino=%ld\n",inode->i_ino);
+ if (vma->vm_flags & VM_SHARED) /* only PAGE_COW or read-only supported now */
+ return -EINVAL;
+ if (vma->vm_offset & (inode->i_sb->s_blocksize - 1))
+ return -EINVAL;
+ if (!inode->i_sb || !S_ISREG(inode->i_mode))
+ return -EACCES;
+ if (!IS_RDONLY(inode)) {
+ inode->i_atime = CURRENT_TIME;
+ /*inode->i_dirt = 1;*/
+ mark_inode_dirty(inode);
+ }
+
+ vma->vm_dentry = dget(file->f_dentry);
+ vma->vm_ops = &dblspace_file_mmap;
+ return 0;
+}
+
+#endif /* from: else / ifdef DMSDOS_USE_READPAGE */
+
diff --git a/src/dblspace_interface.c b/src/dblspace_interface.c
new file mode 100644
index 0000000..39aade8
--- /dev/null
+++ b/src/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/dblspace_ioctl.c b/src/dblspace_ioctl.c
new file mode 100644
index 0000000..6eecc28
--- /dev/null
+++ b/src/dblspace_ioctl.c
@@ -0,0 +1,971 @@
+/*
+dblspace_ioctl.c
+
+DMSDOS CVF-FAT module: ioctl functions (interface for external utilities).
+
+******************************************************************************
+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 __KERNEL__
+#error This file needs __KERNEL__
+#endif
+
+#include <asm/segment.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/msdos_fs.h>
+#include <linux/mm.h>
+#include <asm/semaphore.h>
+#include "dmsdos.h"
+
+#ifdef INTERNAL_DAEMON
+int idmsdosd(void*);
+#define __KERNEL_SYSCALLS__
+#include <linux/unistd.h>
+#endif
+
+extern unsigned long dmsdos_speedup;
+
+int daemon_present=0;
+int must_maintain_list=0;
+int listcount=0;
+Rwlist rwlist[LISTSIZE];
+int rlist=0;
+int wlist=0;
+
+#ifdef INTERNAL_DAEMON
+DECLARE_WAIT_QUEUE_HEAD(daemon_wait);
+DECLARE_WAIT_QUEUE_HEAD(daemon_exit_wait);
+int daemon_go_home=0;
+int internal_daemon_counter=0;
+#else
+int daemon_pid;
+int daemon_task_nr;
+#endif
+
+void dumpcache(void);
+void dump_ccache(void);
+
+DECLARE_MUTEX(listaccess_sem); /* Must be initialized to green light */
+void lock_listaccess(void) {down(&listaccess_sem);}
+void unlock_listaccess(void) {up(&listaccess_sem);}
+
+void log_statistics(void)
+{ log_list_statistics();
+ log_ccache_statistics();
+ log_found_statistics();
+ /*log_other_statistics();*/
+}
+
+#undef DEPRECATED_CODE
+#ifdef DEPRECATED_CODE
+int set_maxcluster(struct super_block*sb,int data)
+{ struct buffer_head*bh,*bh2;
+ int i;
+ unsigned char*pp;
+ unsigned long int sectors;
+ Dblsb*dblsb=MSDOS_SB(sb)->private_data;
+
+ if(dblsb->s_cvf_version>=DRVSP3)
+ { printk(KERN_ERR "DMSDOS: set_maxcluster refused - CVF is not in doublespace or drivespace<=2 format.\n");
+ return -EINVAL;
+ }
+ if(data<2||data>dblsb->s_max_cluster2)return -EINVAL;
+ /* check if higher clusters are unused */
+ for(i=dblsb->s_max_cluster2;i>data;--i)
+ { if(dbl_fat_nextcluster(sb,i,NULL))
+ { printk(KERN_ERR "DMSDOS: set_maxcluster %d refused: cluster %d in use\n",
+ data,i);
+ return -EINVAL;
+ }
+ }
+
+ bh=raw_bread(sb,0);
+ if(bh==NULL)return -EIO;
+ bh2=raw_bread(sb,dblsb->s_bootblock);
+ if(bh2==NULL)
+ { raw_brelse(sb,bh);
+ return -EIO;
+ }
+
+ /* calculate number of sectors */
+ /*sectors=(data-1)<<4;*/
+ sectors=(data-1)*dblsb->s_sectperclust;
+ sectors+=dblsb->s_rootdirentries>>4;
+ pp=&(bh->b_data[14]);
+ sectors+=CHS(pp);
+ pp=&(bh->b_data[22]);
+ sectors+=CHS(pp);
+ bh->b_data[32]=sectors;
+ bh->b_data[33]=sectors>>8;
+ bh->b_data[34]=sectors>>16;
+ bh->b_data[35]=sectors>>24;
+ sectors+=CHS(pp);
+ bh2->b_data[32]=sectors;
+ bh2->b_data[33]=sectors>>8;
+ bh2->b_data[34]=sectors>>16;
+ bh2->b_data[35]=sectors>>24;
+ raw_mark_buffer_dirty(sb,bh,1);
+ raw_mark_buffer_dirty(sb,bh2,1);
+ raw_brelse(sb,bh);
+ raw_brelse(sb,bh2);
+
+ dblsb->s_max_cluster=data;
+ MSDOS_SB(sb)->clusters=data;
+ MSDOS_SB(sb)->free_clusters=-1;
+
+ return 0;
+}
+#endif
+
+void dmsdos_extra_statfs(struct super_block*sb, Dblstat*dblstat)
+{ int sector,size,cluster;
+ Mdfat_entry mde;
+ Dblsb*dblsb=MSDOS_SB(sb)->private_data;
+
+ dblstat->free_sectors=0;
+ dblstat->max_hole=0;
+ for(sector=dblsb->s_datastart;sector<dblsb->s_dataend;
+ ++sector)
+ { if(dbl_bitfat_value(sb,sector,NULL)==0)
+ { ++(dblstat->free_sectors);
+ size=1;
+ while(dbl_bitfat_value(sb,sector+size,NULL)==0)++size;
+ if(size>dblstat->max_hole)dblstat->max_hole=size;
+ sector+=size-1;
+ dblstat->free_sectors+=size-1;
+ }
+ }
+ dblstat->used_sectors=dblsb->s_dataend+1-dblsb->s_datastart
+ -dblstat->free_sectors;
+ dblstat->sectors_lo=0;
+ dblstat->sectors_hi=0;
+ dblstat->compressed_clusters=0;
+ dblstat->uncompressed_clusters=0;
+ dblstat->free_clusters=0;
+ dblstat->used_clusters=0;
+ dblstat->lost_clusters=0;
+ for(cluster=2;cluster<=dblsb->s_max_cluster;++cluster)
+ { if(dbl_fat_nextcluster(sb,cluster,NULL)!=0)
+ { dbl_mdfat_value(sb,cluster,NULL,&mde);
+ if(mde.flags&2)
+ { ++(dblstat->used_clusters);
+ if(mde.flags&1)++(dblstat->uncompressed_clusters);
+ else ++(dblstat->compressed_clusters);
+ dblstat->sectors_hi+=mde.size_hi_minus_1+1;
+ dblstat->sectors_lo+=mde.size_lo_minus_1+1;
+ }
+ else ++(dblstat->lost_clusters);
+ }
+ else ++(dblstat->free_clusters);
+ }
+}
+
+int dmsdos_ioctl_dir(
+ struct inode *dir,
+ struct file *filp,
+ unsigned int cmd,
+ unsigned long data)
+{ unsigned char* idata;
+ struct buffer_head*bh;
+ int newval;
+ int val;
+ int cluster;
+ int sector;
+ Dblstat dblstat;
+ Mdfat_entry mde,dummy;
+ unsigned char *clusterd;
+ int membytes;
+ struct super_block*sb=dir->i_sb;
+ Dblsb*dblsb=MSDOS_SB(sb)->private_data;
+ long lval;
+#ifndef INTERNAL_DAEMON
+ int length;
+ int i;
+ int plist;
+ /*int sectors;*/
+ int rawlength;
+#endif
+
+ idata=(unsigned char*)data;
+
+ switch(cmd)
+ { case DMSDOS_GET_DBLSB:
+ /* check dutil version number */
+ if(verify_area(VERIFY_READ, idata, sizeof(long)))return -EFAULT;
+ /*if(get_fs_long(idata)<DMSDOS_LOWEST_COMPATIBLE_VERSION)*/
+ memcpy_fromfs(&lval,idata,sizeof(long));
+ if(lval<DMSDOS_LOWEST_COMPATIBLE_VERSION)
+ return DMSDOS_VERSION|0x0f000000;
+ if(verify_area(VERIFY_WRITE, idata, sizeof(Dblsb)))return -EFAULT;
+ memcpy_tofs(idata,(unsigned char*)dblsb,sizeof(Dblsb));
+ return DMSDOS_VERSION;
+ case DMSDOS_EXTRA_STATFS:
+ if(verify_area(VERIFY_WRITE, idata, sizeof(Dblstat)))return -EFAULT;
+ dmsdos_extra_statfs(dir->i_sb,&dblstat);
+ memcpy_tofs(idata,&dblstat,sizeof(Dblstat));
+ return 0;
+ case DMSDOS_READ_BLOCK:
+ if(current->euid!=0)return -EPERM;
+ if(verify_area(VERIFY_READ, idata, sizeof(long)))return -EFAULT;
+ /*sector=get_fs_long(idata);*/
+ memcpy_fromfs(&sector,idata,sizeof(long));
+ if(sector<0||sector>dblsb->s_dataend)return -EINVAL;
+ bh=raw_bread(dir->i_sb,sector);
+ if(bh==NULL)return -EIO;
+ if(verify_area(VERIFY_WRITE, idata+sizeof(long),SECTOR_SIZE))return -EFAULT;
+ memcpy_tofs(idata+sizeof(long),bh->b_data,SECTOR_SIZE);
+ raw_brelse(dir->i_sb,bh);
+ return 0;
+ case DMSDOS_WRITE_BLOCK:
+ if(current->euid!=0)return -EPERM;
+ if(verify_area(VERIFY_READ, idata, sizeof(long)+SECTOR_SIZE))return -EFAULT;
+ /*sector=get_fs_long(idata);*/
+ memcpy_fromfs(&sector,idata,sizeof(long));
+ if(sector<0||sector>dblsb->s_dataend)return -EINVAL;
+ bh=raw_bread(dir->i_sb,sector);
+ if(bh==NULL)return -EIO;
+ memcpy_fromfs(bh->b_data,idata+sizeof(long),SECTOR_SIZE);
+ raw_mark_buffer_dirty(dir->i_sb,bh,1);
+ raw_brelse(dir->i_sb,bh);
+ return 0;
+ case DMSDOS_READ_DIRENTRY:
+ case DMSDOS_WRITE_DIRENTRY:
+ printk(KERN_WARNING "DMSDOS: READ/WRITE DIRENTRY ioctl has gone\n");
+ return -EINVAL;
+ case DMSDOS_READ_BITFAT:
+ if(verify_area(VERIFY_READ, idata, sizeof(long)))return -EFAULT;
+ /*sector=get_fs_long(idata);*/
+ memcpy_fromfs(&sector,idata,sizeof(long));
+ if(sector<0||sector>dblsb->s_dataend)return -EINVAL;
+ val=dbl_bitfat_value(sb,sector,NULL);
+ if(verify_area(VERIFY_WRITE, idata+sizeof(long),sizeof(long)))return -EFAULT;
+ /*put_fs_long(val,idata+sizeof(long));*/
+ memcpy_tofs(idata+sizeof(long),&val,sizeof(long));
+ return 0;
+ case DMSDOS_WRITE_BITFAT:
+ if(current->euid!=0)return -EPERM;
+ if(verify_area(VERIFY_READ, idata, 2*sizeof(long)))return -EFAULT;
+ /*sector=get_fs_long(idata);*/
+ memcpy_fromfs(&sector,idata,sizeof(long));
+ if(sector<0||sector>dblsb->s_dataend)return -EINVAL;
+ /*newval=get_fs_long(idata+sizeof(long));*/
+ memcpy_fromfs(&newval,idata+sizeof(long),sizeof(long));
+ dbl_bitfat_value(sb,sector,&newval);
+ return 0;
+ case DMSDOS_READ_MDFAT:
+ if(verify_area(VERIFY_READ, idata, sizeof(long)))return -EFAULT;
+ /*cluster=get_fs_long(idata);*/
+ memcpy_fromfs(&cluster,idata,sizeof(long));
+ if(cluster>dblsb->s_max_cluster)return -EINVAL;
+ dbl_mdfat_value(sb,cluster,NULL,&mde);
+ if(verify_area(VERIFY_WRITE,idata+sizeof(long),sizeof(Mdfat_entry)))return -EFAULT;
+ memcpy_fromfs(&lval,idata+sizeof(long),sizeof(long));
+ memcpy_tofs((Mdfat_entry*)lval,&mde,sizeof(Mdfat_entry));
+ return 0;
+ case DMSDOS_WRITE_MDFAT:
+ if(current->euid!=0)return -EPERM;
+ if(verify_area(VERIFY_READ, idata, sizeof(long)+sizeof(Mdfat_entry)))return -EFAULT;
+ /*cluster=get_fs_long(idata);*/
+ memcpy_fromfs(&cluster,idata,sizeof(long));
+ if(cluster>dblsb->s_max_cluster)return -EINVAL;
+ memcpy_fromfs(&lval,idata+sizeof(long),sizeof(long));
+ memcpy_fromfs(&mde,(Mdfat_entry*)lval,
+ sizeof(Mdfat_entry));
+ dbl_mdfat_value(sb,cluster,&mde,&dummy);
+ return 0;
+ case DMSDOS_READ_DFAT:
+ if(verify_area(VERIFY_READ, idata, sizeof(long)))return -EFAULT;
+ /*cluster=get_fs_long(idata);*/
+ memcpy_fromfs(&cluster,idata,sizeof(long));
+ if(cluster>dblsb->s_max_cluster)return -EINVAL;
+ val=dbl_fat_nextcluster(sb,cluster,NULL);
+ if(verify_area(VERIFY_WRITE, idata+sizeof(long),sizeof(long)))return -EFAULT;
+ /*put_fs_long(val,idata+sizeof(long));*/
+ memcpy_tofs(idata+sizeof(long),&val,sizeof(long));
+ return 0;
+ case DMSDOS_WRITE_DFAT:
+ if(current->euid!=0)return -EPERM;
+ if(verify_area(VERIFY_READ, idata, 2*sizeof(long)))return -EFAULT;
+ /*cluster=get_fs_long(idata);*/
+ memcpy_fromfs(&cluster,idata,sizeof(long));
+ if(cluster>dblsb->s_max_cluster)return -EINVAL;
+ /*newval=get_fs_long(idata+sizeof(long));*/
+ memcpy_fromfs(&newval,idata+sizeof(long),sizeof(long));
+ dbl_fat_nextcluster(sb,cluster,&newval);
+ return 0;
+ case DMSDOS_READ_CLUSTER:
+ if(current->euid!=0)return -EPERM;
+ if(verify_area(VERIFY_READ, idata, sizeof(long)))return -EFAULT;
+ /*cluster=get_fs_long(idata);*/
+ memcpy_fromfs(&cluster,idata,sizeof(long));
+ if(cluster < 0 || cluster>dblsb->s_max_cluster)
+ return -EINVAL;
+ membytes=SECTOR_SIZE*dblsb->s_sectperclust;
+ if(verify_area(VERIFY_WRITE, idata+sizeof(long),membytes))return -EFAULT;
+ if((clusterd=(unsigned char*)MALLOC(membytes))==NULL)
+ { printk(KERN_ERR "DMSDOS: ioctl: read_cluster: no memory!\n");
+ return -EIO;
+ }
+ val=dmsdos_read_cluster(dir->i_sb,clusterd,cluster);
+ if (val >= 0)
+ memcpy_tofs(idata+sizeof(long), clusterd, membytes);
+ FREE(clusterd);
+ return val;
+ case DMSDOS_SET_COMP:
+ if(current->euid!=0)return -EPERM;
+ if(dblsb->s_comp!=READ_ONLY&&data==READ_ONLY)sync_cluster_cache(0);
+ dblsb->s_comp=data;
+ if(data==READ_ONLY)sb->s_flags |= MS_RDONLY;
+ else sb->s_flags&=~MS_RDONLY;
+ return 0;
+ case DMSDOS_SET_CF:
+ if(current->euid!=0)return -EPERM;
+ if(data>=12u)return -EINVAL;
+ dblsb->s_cfaktor=data;
+ return 0;
+ case DMSDOS_SIMPLE_CHECK:
+ if(verify_area(VERIFY_READ, idata, sizeof(long)))return -EFAULT;
+ memcpy_fromfs(&lval,idata,sizeof(long));
+ val=simple_check(dir->i_sb,lval);
+ if(verify_area(VERIFY_WRITE, idata, sizeof(long)))return -EFAULT;
+ /*put_fs_long(val,idata);*/
+ memcpy_tofs(idata,&val,sizeof(long));
+ return 0;
+ case DMSDOS_DUMPCACHE:
+ dumpcache();
+ dump_ccache();
+ return 0;
+ case DMSDOS_D_ASK:
+ #ifdef INTERNAL_DAEMON
+ return -EINVAL;
+ #else
+ if(current->euid!=0)return -EPERM;
+ /* dmsdosd says it is present and ready */
+ if(current->pid!=data)
+ { printk(KERN_ERR "DMSDOS: daemon is lying about its pid\n");
+ return -EINVAL;
+ }
+ daemon_present=1;
+ daemon_pid=current->pid;
+ LOG_DAEMON("DMSDOS: D_ASK\n");
+ return 0;
+ #endif
+ case DMSDOS_D_READ:
+ #ifdef INTERNAL_DAEMON
+ return -EINVAL;
+ #else
+ if(current->euid!=0)return -EPERM;
+ lock_listaccess();
+ /*search next valid entry*/
+ for(i=LISTSIZE;i>0;--i)
+ { if(rwlist[rlist].flag==D_VALID)
+ { /* check in mdfat that cluster is actually used */
+ dbl_mdfat_value(rwlist[rlist].sb,rwlist[rlist].clusternr,
+ NULL,&mde);
+ if((mde.flags&3)==3)goto vr_found; /* used and uncompressed */
+ rwlist[rlist].flag=D_EMPTY; /* remove - it's garbage */
+ --listcount;
+ LOG_DAEMON("DMSDOS: D_READ: removing garbage entry cluster=%d\n",
+ rwlist[rlist].clusternr);
+ }
+ /*rlist=(rlist+1)&(LISTSIZE-1);*/
+ rlist++;if(rlist>=LISTSIZE)rlist=0;
+ }
+ unlock_listaccess();
+ return 0;
+ vr_found:
+ cluster=rwlist[rlist].clusternr;
+ /* yes we change sb here */
+ sb=rwlist[rlist].sb;
+ dblsb=MSDOS_SB(sb)->private_data;
+ length=rwlist[rlist].length;
+ clusterd=MALLOC(dblsb->s_sectperclust*SECTOR_SIZE);
+ if(clusterd==NULL)
+ { printk(KERN_ERR "DMSDOS: ioctl: D_READ: no memory!\n");
+ unlock_listaccess();
+ return 0;
+ }
+ if((i=dmsdos_read_cluster(sb,clusterd,cluster))<0)
+ { printk(KERN_ERR "DMSDOS: ioctl: D_READ: read_cluster failed!\n");
+ rwlist[rlist].flag=D_EMPTY;
+ --listcount;
+ unlock_listaccess();
+ FREE(clusterd);
+ return 0;
+ }
+ if(length<0)length=i; /* if invalid length, use the read value */
+ rwlist[rlist].flag=D_IN_D_ACTION;
+ if(verify_area(VERIFY_WRITE, idata, 3*sizeof(long)+length))
+ { unlock_listaccess();
+ FREE(clusterd);
+ return -EFAULT;
+ }
+ /*put_fs_long(rlist,idata);*/
+ memcpy_tofs(idata,&rlist,sizeof(long));
+ /*put_fs_long(length,idata+sizeof(long));*/
+ memcpy_tofs(idata+sizeof(long),&length,sizeof(long));
+ /*put_fs_long(rwlist[rlist].method,idata+2*sizeof(long));*/
+ memcpy_tofs(idata+2*sizeof(long),&(rwlist[rlist].method),sizeof(long));
+ memcpy_tofs(idata+3*sizeof(long),clusterd,length);
+ unlock_listaccess();
+ FREE(clusterd);
+ return 1;
+#endif
+ case DMSDOS_D_WRITE:
+#ifdef INTERNAL_DAEMON
+ return -EINVAL;
+#else
+ if(current->euid!=0)return -EPERM;
+ if(verify_area(VERIFY_READ,idata,3*sizeof(long)))return -EFAULT;
+ lock_listaccess();
+ /*plist=get_fs_long(idata);*/
+ memcpy_fromfs(&plist,idata,sizeof(long));
+ if(rwlist[plist].flag==D_OVERWRITTEN){rwlist[plist].flag=D_EMPTY;--listcount;}
+ if(rwlist[plist].flag!=D_IN_D_ACTION)
+ { LOG_DAEMON("DMSDOS: D_WRITE: Entry not in action, flag=%d cluster=%d\n",
+ rwlist[plist].flag,rwlist[plist].clusternr);
+ unlock_listaccess();
+ return 0;
+ }
+
+ /* will be freed in any case later, so: */
+ --listcount;
+
+ /*rawlength=get_fs_long(idata+sizeof(long));*/
+ memcpy_fromfs(&rawlength,idata+sizeof(long),sizeof(long));
+ if(rawlength==0)
+ { /* data were uncompressible */
+ rwlist[plist].flag=D_EMPTY;
+ unlock_listaccess();
+ return 0;
+ }
+ /* check that cluster is used */
+ dbl_mdfat_value(rwlist[plist].sb,rwlist[plist].clusternr,
+ NULL,&mde);
+ if((mde.flags&3)!=3)
+ { rwlist[plist].flag=D_EMPTY; /* remove - it's garbage */
+ LOG_DAEMON("DMSDOS: D_WRITE: removing garbage entry cluster=%d\n",
+ rwlist[plist].clusternr);
+ unlock_listaccess();
+ return 0;
+ }
+ if(verify_area(VERIFY_READ,idata+3*sizeof(long),rawlength))
+ { rwlist[plist].flag=D_EMPTY;
+ unlock_listaccess();
+ return -EFAULT;
+ }
+ clusterd=MALLOC(rawlength);
+ if(clusterd==NULL)
+ { printk(KERN_ERR "DMSDOS: ioctl: D_WRITE: no memory!\n");
+ rwlist[plist].flag=D_EMPTY;
+ unlock_listaccess();
+ return 0;
+ }
+ length=rwlist[plist].length;
+ cluster=rwlist[plist].clusternr;
+ sb=rwlist[plist].sb;
+ memcpy_fromfs(clusterd,idata+3*sizeof(long),rawlength);
+ rwlist[plist].flag=D_EMPTY;
+ unlock_listaccess();
+ /* this may call try_daemon via ccache code (freeing a ccache slot) */
+ daemon_write_cluster(sb,clusterd,length,
+ cluster,
+ rawlength);
+ FREE(clusterd);
+ return 0;
+#endif
+ case DMSDOS_D_EXIT:
+#ifdef INTERNAL_DAEMON
+ return -EINVAL;
+#else
+ if(current->euid!=0)return -EPERM;
+ /* dmsdosd is saying good-bye */
+ daemon_present=0;
+ LOG_DAEMON("DMSDOS: D_EXIT\n");
+ return 0;
+#endif
+ case DMSDOS_MOVEBACK:
+ printk(KERN_WARNING "DMSDOS: MOVEBACK ioctl has gone\n");
+ return -EINVAL;
+ case DMSDOS_SET_MAXCLUSTER:
+ /*if(current->euid!=0)return -EPERM;
+ return set_maxcluster(dir->i_sb,data);*/
+ printk(KERN_WARNING "DMSDOS: SETMAXCLUSTER ioctl has gone.\n");
+ return -EINVAL;
+ case DMSDOS_FREE_IDLE_CACHE:
+ if(current->euid!=0)return -EPERM;
+ free_idle_cache();
+ return 0;
+ case DMSDOS_SET_LOGLEVEL:
+ if(current->euid!=0)return -EPERM;
+ loglevel=data;
+ printk(KERN_INFO "DMSDOS: ioctl: loglevel set to 0x%lx.\n",loglevel);
+ return 0;
+ case DMSDOS_SET_SPEEDUP:
+ if(current->euid!=0)return -EPERM;
+ dmsdos_speedup=data;
+ printk(KERN_INFO "DMSDOS: ioctl: speedup set to 0x%lx.\n",dmsdos_speedup);
+ return 0;
+ case DMSDOS_SYNC_CCACHE:
+ sync_cluster_cache(data);
+ return 0;
+ case DMSDOS_LOG_STATISTICS:
+ if(current->euid!=0)return -EPERM;
+ log_statistics();
+ return 0;
+ case DMSDOS_RECOMPRESS:
+ printk(KERN_WARNING "DMSDOS: RECOMPRESS ioctl has gone\n");
+ return -EINVAL;
+ case DMSDOS_REPORT_MEMORY:
+ { Memuse memuse;
+ if(verify_area(VERIFY_WRITE,idata,sizeof(Memuse)))return -EFAULT;
+ get_memory_usage_acache(&(memuse.acachebytes),&(memuse.max_acachebytes));
+ get_memory_usage_ccache(&(memuse.ccachebytes),&(memuse.max_ccachebytes));
+ memcpy_tofs(idata,&memuse,sizeof(Memuse));
+ return 0;
+ }
+ default:
+ return -EINVAL;
+ }
+
+}
+
+void remove_from_daemon_list(struct super_block*sb,int clusternr)
+{ int i;
+
+ if(must_maintain_list==0)return;
+
+ lock_listaccess();
+
+ /* check list for existing entry and mark it as overwritten */
+ for(i=0;i<LISTSIZE;++i)
+ { if(rwlist[i].flag!=D_EMPTY)
+ if(rwlist[i].clusternr==clusternr&&rwlist[i].sb->s_dev==sb->s_dev)
+ {
+ if(rwlist[i].flag==D_IN_D_ACTION){rwlist[i].flag=D_OVERWRITTEN;break;}
+ if(rwlist[i].flag==D_VALID){rwlist[i].flag=D_EMPTY;--listcount;break;}
+ }
+ }
+
+ if(daemon_present==0)
+ { if(listcount==0)must_maintain_list=0;
+ }
+
+ unlock_listaccess();
+}
+
+int lastawake=0;
+int try_daemon(struct super_block*sb,int clusternr, int length, int method)
+{ int i;
+
+ if(daemon_present==0&&must_maintain_list==0)return 0;
+
+ lock_listaccess();
+
+ must_maintain_list=1;
+
+ /* check list for existing entry and mark it as overwritten */
+/* no longer necessary here......
+ for(i=0;i<LISTSIZE;++i)
+ { if(rwlist[i].clusternr==clusternr&&rwlist[i].sb->s_dev==sb->s_dev)
+ {
+ if(rwlist[i].flag==D_IN_D_ACTION){rwlist[i].flag=D_OVERWRITTEN;break;}
+ if(rwlist[i].flag==D_VALID){rwlist[i].flag=D_EMPTY;--listcount;break;}
+ }
+ }
+*/
+
+ if(daemon_present==0||listcount==LISTSIZE)
+ {
+ if(listcount==0)must_maintain_list=0;
+ unlock_listaccess();
+ return 0;
+ }
+
+ /* find empty slot in list */
+ for(i=LISTSIZE;i>0;--i)
+ { if(rwlist[wlist].flag==D_EMPTY) goto w_found;
+ /*wlist=(wlist+1)&(LISTSIZE-1);*/
+ wlist++;if(wlist>=LISTSIZE)wlist=0;
+ }
+ /*this shouldn't happen - otherwise there's a count error */
+ printk(KERN_WARNING "DMSDOS: try_daemon: no empty slot found, listcount corrected.\n");
+ listcount=LISTSIZE;
+ unlock_listaccess();
+ return 0;
+
+ w_found:
+ ++listcount;
+ rwlist[wlist].clusternr=clusternr;
+ rwlist[wlist].sb=sb;
+ rwlist[wlist].length=length;
+ rwlist[wlist].method=method;
+ rwlist[wlist].flag=D_VALID;
+ unlock_listaccess();
+
+ /* check whether or not to awake the daemon -
+ strategy is this:
+ * don't awake it in periods below 5 secs
+ * don't awake it for just a little data
+ */
+ if(lastawake+5>CURRENT_TIME||listcount<LISTSIZE/2)return 1;
+ lastawake=CURRENT_TIME;
+#ifdef INTERNAL_DAEMON
+ wake_up(&daemon_wait);
+#else
+ i=kill_proc(daemon_pid,SIGUSR1,1);
+ if(i<0)
+ { printk(KERN_WARNING "DMSDOS: try_daemon: kill_proc daemon_pid=%d failed with error code %d, assuming daemon has died\n",
+ daemon_pid,-i);
+ daemon_present=0;
+ }
+#endif
+ return 1;
+}
+
+void clear_list_dev(struct super_block*sb)
+{ int i;
+
+ lock_listaccess();
+ for(i=0;i<LISTSIZE;++i)
+ { if(rwlist[i].flag!=D_EMPTY)
+ { if(rwlist[i].sb)printk(KERN_ERR "DMSDOS: clear_list_dev: Uhh, sb==NULL ...\n");
+ else
+ if(rwlist[i].sb->s_dev==sb->s_dev)
+ { rwlist[i].flag=D_EMPTY;
+ --listcount;
+ }
+ }
+ }
+ unlock_listaccess();
+}
+
+void init_daemon(void)
+{ int i;
+
+ if(daemon_present)
+ { printk(KERN_INFO "DMSDOS: init_daemon: daemon already present\n");
+#ifdef INTERNAL_DAEMON
+ ++internal_daemon_counter;
+#endif
+ return;
+ }
+ lock_listaccess();
+ LOG_REST("DMSDOS: clearing rwlist...\n");
+ for(i=0;i<LISTSIZE;++i)rwlist[i].flag=D_EMPTY;
+ listcount=0;
+ unlock_listaccess();
+
+#ifdef INTERNAL_DAEMON
+ /*daemon_present=1...this is maintained by idmsdosd itself*/;
+ internal_daemon_counter=1;
+ /* fire up internal daemon */
+ printk(KERN_NOTICE "DMSDOS: starting internal daemon...\n");
+ daemon_go_home=0;
+ kernel_thread(idmsdosd,NULL,0);
+#endif
+}
+
+void log_list_statistics()
+{ int j;
+ int empty;
+ int valid;
+ int in_action;
+ int overwritten;
+
+ lock_listaccess();
+
+ printk(KERN_INFO "DMSDOS: list statistics:\n");
+ printk(KERN_INFO "daemon_present=%d must_maintain_list=%d listcount=%d\n",
+ daemon_present,must_maintain_list,listcount);
+ empty=0;
+ valid=0;
+ in_action=0;
+ overwritten=0;
+
+ for(j=0;j<LISTSIZE;++j)
+ { switch(rwlist[j].flag)
+ { case D_EMPTY: ++empty; break;
+ case D_VALID: ++valid; break;
+ case D_IN_D_ACTION: ++in_action; break;
+ case D_OVERWRITTEN: ++overwritten; break;
+ default: printk(KERN_ERR "DMSDOS: log_list_statistics: cannot happen.\n");
+ }
+ }
+ printk(KERN_INFO "sum: empty=%d valid=%d in_action=%d overwritten=%d\n",
+ empty,valid,in_action,overwritten);
+
+ unlock_listaccess();
+}
+
+#ifdef INTERNAL_DAEMON
+
+int internal_d_read(int*val1, int*val2, int*val3,
+ unsigned char*clusterd)
+{ int i;
+ int cluster;
+ Mdfat_entry mde;
+ int length;
+ int method;
+ struct super_block*sb=NULL;
+
+ lock_listaccess();
+ /*search next valid entry*/
+ for(i=LISTSIZE;i>0;--i)
+ { if(rwlist[rlist].flag==D_VALID)
+ { /* check in mdfat that cluster is actually used */
+ sb=rwlist[rlist].sb;
+ if(sb)
+ { dbl_mdfat_value(sb,rwlist[rlist].clusternr,
+ NULL,&mde);
+ if((mde.flags&3)==3)goto vr_found; /* used and uncompressed */
+ }
+ rwlist[rlist].flag=D_EMPTY; /* remove - it's garbage */
+ --listcount;
+ LOG_DAEMON("DMSDOS: D_READ: removing garbage entry cluster=%d\n",
+ rwlist[rlist].clusternr);
+ }
+ /*rlist=(rlist+1)&(LISTSIZE-1);*/
+ rlist++;if(rlist>=LISTSIZE)rlist=0;
+ }
+ unlock_listaccess();
+ return 0;
+ vr_found:
+ cluster=rwlist[rlist].clusternr;
+ sb=rwlist[rlist].sb;
+ length=rwlist[rlist].length;
+ method=rwlist[rlist].method;
+ if((i=dmsdos_read_cluster(sb,clusterd,cluster))<0)
+ { printk(KERN_ERR "DMSDOS: ioctl: D_READ: read_cluster failed!\n");
+ rwlist[rlist].flag=D_EMPTY;
+ --listcount;
+ unlock_listaccess();
+ return 0;
+ }
+ if(length<0)length=i; /* if invalid length, use read value */
+ rwlist[rlist].flag=D_IN_D_ACTION;
+ *val1=rlist; /*put_fs_long(rlist,idata);*/
+ *val2=length; /*put_fs_long(length,idata+sizeof(long));*/
+ *val3=method; /*put_fs_long(rwlist[rlist].method,idata+2*sizeof(long));*/
+ unlock_listaccess();
+ return 1;
+}
+
+int internal_d_write(int plist, int rawlength, unsigned char*clusterd)
+{ Mdfat_entry mde;
+ int length;
+ int cluster;
+ struct super_block*sb=NULL;
+
+ lock_listaccess();
+ if(rwlist[plist].flag==D_OVERWRITTEN){rwlist[plist].flag=D_EMPTY;--listcount;}
+ if(rwlist[plist].flag!=D_IN_D_ACTION)
+ { LOG_DAEMON("DMSDOS: D_WRITE: Entry not in action, flag=%d cluster=%d\n",
+ rwlist[plist].flag,rwlist[plist].clusternr);
+ unlock_listaccess();
+ return 0;
+ }
+
+ /* will be freed in any case later, so: */
+ --listcount;
+
+ if(rawlength==0)
+ { /* data were uncompressible */
+ rwlist[plist].flag=D_EMPTY;
+ unlock_listaccess();
+ return 0;
+ }
+ /* check that cluster is used */
+ sb=rwlist[plist].sb;
+ if(sb==NULL)goto shitt;
+ dbl_mdfat_value(sb,rwlist[plist].clusternr,
+ NULL,&mde);
+ if((mde.flags&3)!=3)
+ { shitt:
+ rwlist[plist].flag=D_EMPTY; /* remove - it's garbage */
+ LOG_DAEMON("DMSDOS: D_WRITE: removing garbage entry cluster=%d\n",
+ rwlist[plist].clusternr);
+ unlock_listaccess();
+ return 0;
+ }
+ length=rwlist[plist].length;
+ cluster=rwlist[plist].clusternr;
+ rwlist[plist].flag=D_EMPTY;
+ unlock_listaccess();
+ /* this may call try_daemon via ccache code (freeing a ccache slot) */
+ daemon_write_cluster(sb,clusterd,length,cluster,
+ rawlength);
+ return 0;
+}
+
+typedef struct
+{ long val1;
+ long val2;
+ long val3;
+ unsigned char data[32*1024];
+} Cdata;
+
+/* we need the memory always - there's only one process using it - idmsdosd */
+Cdata cdata;
+Cdata ckdata;
+
+int get_and_compress_one(void)
+{ int ret;
+ int handle;
+ int length;
+ int size;
+ int method;
+
+ /* get cluster to compress */
+ LOG_DAEMON("idmsdosd: Trying to read...\n");
+ ret=internal_d_read(&handle,&length,&method,cdata.data);
+ if(ret!=1)
+ { LOG_DAEMON("idmsdosd: nothing there - D_READ ioctl returned %d\n",ret);
+ return ret;
+ }
+
+ size=(length-1)/512+1;
+ LOG_DAEMON("idmsdosd: compressing...\n");
+ ret=
+#ifdef DMSDOS_CONFIG_STAC
+ (method==SD_3||method==SD_4) ?
+ stac_compress(cdata.data,length,ckdata.data,
+ sizeof(ckdata.data),method,11) :
+#endif
+ dbl_compress(ckdata.data,cdata.data,size,method,11)*512;
+ LOG_DAEMON("idmsdosd: compress %X from %d returned %d\n",
+ method,length,ret);
+ if(ret<0)ret=0; /* compression failed */
+ LOG_DAEMON("idmsdosd: writing...\n");
+ internal_d_write(handle,ret,ckdata.data);
+
+ return 1; /* one cluster compressed */
+}
+
+struct timer_list idmsdosd_timer;
+
+static void idmsdosd_timer_function(unsigned long data)
+{
+ /* do something */
+ /*printk(KERN_DEBUG "DMSDOS: idmsdosd_timer_function: doing something :)\n");*/
+
+ /* wake up daemon */
+ wake_up(&daemon_wait);
+
+ del_timer(&idmsdosd_timer);
+ idmsdosd_timer.expires=jiffies + (IDMSDOSD_TIME * HZ);
+ add_timer(&idmsdosd_timer);
+}
+
+int idmsdosd(void*dummy)
+{
+ /* throw away some things from the calling process */
+ /* otherwise the root fs cannot be unmounted on reboot ... urgh*/
+ exit_files(current);
+ exit_fs(current);
+ exit_sighand(current);
+ exit_mm(current);
+
+ /*
+ * We have a bare-bones task_struct, and really should fill
+ * in a few more things so "top" and /proc/2/{exe,root,cwd}
+ * display semi-sane things. Not real crucial though...
+ */
+ current->session = 1;
+ current->pgrp = 1;
+ sprintf(current->comm, "dmsdosd");
+
+#undef NEED_LOCK_KERNEL
+ /* do we really need this ??? The code was copied from kflushd. */
+#ifdef NEED_LOCK_KERNEL
+ /*
+ * As a kernel thread we want to tamper with system buffers
+ * and other internals and thus be subject to the SMP locking
+ * rules. (On a uniprocessor box this does nothing).
+ */
+#ifdef __SMP__
+ lock_kernel();
+ syscall_count++;
+#endif
+#endif
+
+ daemon_present=1;
+
+ init_timer(&idmsdosd_timer);
+ idmsdosd_timer.function=idmsdosd_timer_function;
+ idmsdosd_timer.expires=jiffies + (IDMSDOSD_TIME * HZ);
+ add_timer(&idmsdosd_timer);
+
+ for(;;)
+ { while(get_and_compress_one()==1);
+ /* don't kill the system performance when nothing to compress */
+ { LOG_DAEMON("idmsdosd: sleeping...\n");
+ interruptible_sleep_on(&daemon_wait);
+ if(daemon_go_home)break;
+ /* throw away long idle mdfat/dfat/bitfat sectors and clusters */
+ free_idle_cache();
+ }
+ }
+
+ del_timer(&idmsdosd_timer);
+ daemon_present=0;
+
+ LOG_DAEMON("idmsdosd: exiting...\n");
+ wake_up(&daemon_exit_wait);
+ return 0;
+}
+
+void remove_internal_daemon(void)
+{ if(daemon_present)
+ { printk(KERN_NOTICE "DMSDOS: killing internal daemon...\n");
+ daemon_go_home=1;
+ wake_up(&daemon_wait);
+ interruptible_sleep_on(&daemon_exit_wait);
+ /* this seems to work - don't ask me why :) */
+ }
+}
+
+#endif
+
+void exit_daemon(void)
+{
+#ifdef INTERNAL_DAEMON
+ --internal_daemon_counter;
+ if(internal_daemon_counter<0)
+ { printk(KERN_WARNING "DMSDOS: exit_daemon: counter<0 ???\n");
+ internal_daemon_counter=0;
+ }
+ if(internal_daemon_counter==0)remove_internal_daemon();
+#endif
+}
+
+void force_exit_daemon(void)
+{
+#ifdef INTERNAL_DAEMON
+ internal_daemon_counter=0;
+ remove_internal_daemon();
+#endif
+}
diff --git a/src/dblspace_methsq.c b/src/dblspace_methsq.c
new file mode 100644
index 0000000..43422b1
--- /dev/null
+++ b/src/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/dblspace_tables.c b/src/dblspace_tables.c
new file mode 100644
index 0000000..492e3ff
--- /dev/null
+++ b/src/dblspace_tables.c
@@ -0,0 +1,760 @@
+/*
+dblspace_tables.c
+
+DMSDOS CVF-FAT module: [d|md|bit]fat access functions.
+
+******************************************************************************
+DMSDOS (compressed MSDOS filesystem support) for Linux
+written 1995-1998 by Frank Gockel and Pavel Pisa
+
+ (C) Copyright 1995-1998 by Frank Gockel
+ (C) Copyright 1996-1998 by Pavel Pisa
+
+Some code of dmsdos has been copied from the msdos filesystem
+so there are the following additional copyrights:
+
+ (C) Copyright 1992,1993 by Werner Almesberger (msdos filesystem)
+ (C) Copyright 1994,1995 by Jacques Gelinas (mmap code)
+ (C) Copyright 1992-1995 by Linus Torvalds
+
+DMSDOS was inspired by the THS filesystem (a simple doublespace
+DS-0-2 compressed read-only filesystem) written 1994 by Thomas Scheuermann.
+
+The DMSDOS code is distributed under the Gnu General Public Licence.
+See file COPYING for details.
+******************************************************************************
+
+*/
+
+#ifdef __KERNEL__
+#include <linux/sched.h>
+#include <linux/ctype.h>
+#include <linux/major.h>
+#include <linux/blkdev.h>
+#include <linux/fs.h>
+#include <linux/stat.h>
+#include <linux/locks.h>
+#include <asm/segment.h>
+#include <linux/mm.h>
+#include <linux/malloc.h>
+#include <linux/string.h>
+#include <linux/msdos_fs.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/shm.h>
+#include <linux/mman.h>
+#include <asm/system.h>
+#include <asm/semaphore.h>
+#endif
+
+#include "dmsdos.h"
+
+#ifdef __DMSDOS_LIB__
+/* some interface hacks */
+#include"lib_interface.h"
+#include<malloc.h>
+#include<time.h>
+#include<errno.h>
+#endif
+
+
+Acache mdfat[MDFATCACHESIZE];
+Acache dfat[DFATCACHESIZE];
+Acache bitfat[BITFATCACHESIZE];
+
+extern unsigned long int dmsdos_speedup;
+
+#ifdef __DMSDOS_LIB__
+
+/* we don't need locking in the library */
+void lock_mdfat(void) {}
+void unlock_mdfat(void) {}
+void lock_dfat(void) {}
+void unlock_dfat(void) {}
+void lock_bitfat(void) {}
+void unlock_bitfat(void) {}
+
+#else
+
+DECLARE_MUTEX(mdfat_sem); /* Must be initialized to green light */
+void lock_mdfat(void) {down(&mdfat_sem);}
+void unlock_mdfat(void) {up(&mdfat_sem);}
+
+DECLARE_MUTEX(dfat_sem); /* Must be initialized to green light */
+void lock_dfat(void) {down(&dfat_sem);}
+void unlock_dfat(void) {up(&dfat_sem);}
+
+DECLARE_MUTEX(bitfat_sem); /* Must be initialized to green light */
+void lock_bitfat(void) {down(&bitfat_sem);}
+void unlock_bitfat(void) {up(&bitfat_sem);}
+
+#endif /* else / __DMSDOS_LIB__ */
+
+int acache_get(struct super_block*sb, Acache*acache, int area, int never,
+ int cachesize)
+{ unsigned long min_time;
+ unsigned int min_acc;
+ int index;
+ int i;
+
+ LOG_ACACHE("DMSDOS: acache_get area=%d never=%d\n",area,never);
+
+ min_time=acache[0].a_time;
+ min_acc=acache[0].a_acc;
+ index=0;
+ if(never==0)
+ { min_time=acache[1].a_time;
+ min_acc=acache[1].a_acc;
+ index=1;
+ }
+ /* find area and dev in cache */
+ for(i=0;i<cachesize;++i)
+ { if( ( acache[i].a_time<min_time||
+ (acache[i].a_time==min_time&&acache[i].a_acc<min_acc)
+ ) &&never!=i)
+ { min_time=acache[i].a_time;
+ min_acc=acache[i].a_acc;
+ index=i;
+ }
+ if(acache[i].a_buffer!=NULL&&area==acache[i].a_area&&sb->s_dev==acache[i].a_sb->s_dev)
+ { /* found */
+ if(acache[i].a_time==CURRENT_TIME)++acache[i].a_acc;
+ else
+ { acache[i].a_time=CURRENT_TIME;
+ acache[i].a_acc=0;
+ }
+ index=i;
+ return index;
+ }
+ }
+ /* index = least recently used entry number */
+ if(acache[index].a_buffer!=NULL)
+ raw_brelse(acache[index].a_sb,acache[index].a_buffer);
+ LOG_ACACHE("DMSDOS: acache_get: reading area %d\n",area);
+ if((acache[index].a_buffer=raw_bread(sb,area))==NULL)
+ { printk(KERN_ERR "DMSDOS: unable to read acache area=%d\n",area);
+ return -EIO;
+ }
+ acache[index].a_area=area;
+ acache[index].a_time=CURRENT_TIME;
+ acache[index].a_acc=0;
+ acache[index].a_sb=sb;
+ return index;
+}
+
+void u_dumpcache(Acache*c)
+{ printk(KERN_INFO "area=%d time=%ld acc=%d buffer=%p dev=0x%x\n",c->a_area,c->a_time,
+ c->a_acc,c->a_buffer,
+ /* check validity of sb before dereferencing to s_dev :) */
+ (c->a_buffer!=NULL&&c->a_sb!=NULL)?c->a_sb->s_dev:0);
+}
+
+void dumpcache(void)
+{ int i;
+
+ printk(KERN_INFO "DMSDOS: mdfat cache:\n");
+ for(i=0;i<MDFATCACHESIZE;++i)u_dumpcache(&(mdfat[i]));
+ printk(KERN_INFO "DMSDOS: dfat cache:\n");
+ for(i=0;i<DFATCACHESIZE;++i)u_dumpcache(&(dfat[i]));
+ printk(KERN_INFO "DMSDOS: bitfat cache:\n");
+ for(i=0;i<BITFATCACHESIZE;++i)u_dumpcache(&(bitfat[i]));
+}
+
+#ifndef __DMSDOS_LIB__
+void get_memory_usage_acache(int*size,int*max)
+{ int i;
+ int used=0;
+
+ for(i=0;i<MDFATCACHESIZE;++i)if(mdfat[i].a_buffer)++used;
+ for(i=0;i<DFATCACHESIZE;++i)if(dfat[i].a_buffer)++used;
+ for(i=0;i<BITFATCACHESIZE;++i)if(bitfat[i].a_buffer)++used;
+
+ if(size)*size=used*SECTOR_SIZE;
+ if(max)*max=(MDFATCACHESIZE+DFATCACHESIZE+BITFATCACHESIZE)*SECTOR_SIZE;
+}
+
+void u_free_idle_cache(Acache*c)
+{ if(c->a_buffer!=NULL&&c->a_time-CURRENT_TIME>MAX_CACHE_TIME)
+ { raw_brelse(c->a_sb,c->a_buffer);
+ c->a_buffer=NULL;
+ c->a_time=0;
+ c->a_acc=0;
+ }
+}
+
+void free_idle_cache(void)
+{ int i;
+
+ lock_mdfat();
+ for(i=0;i<MDFATCACHESIZE;++i)u_free_idle_cache(&(mdfat[i]));
+ unlock_mdfat();
+ lock_dfat();
+ for(i=0;i<DFATCACHESIZE;++i)u_free_idle_cache(&(dfat[i]));
+ unlock_dfat();
+ lock_bitfat();
+ for(i=0;i<BITFATCACHESIZE;++i)u_free_idle_cache(&(bitfat[i]));
+ unlock_bitfat();
+
+ /* handle cluster cache */
+ free_idle_ccache();
+}
+#endif
+
+int dbl_mdfat_value(struct super_block* sb,int clusternr,
+ Mdfat_entry*new,
+ Mdfat_entry*mde)
+{ int area;
+ int pos;
+ int offset;
+ int merk_i;
+#ifdef DMSDOS_CONFIG_STAC
+ int i;
+ int merk_i2;
+ int nr_of_bytes;
+#endif
+#ifdef DMSDOS_CONFIG_DBL
+ unsigned char * pp;
+#endif
+#ifdef DMSDOS_CONFIG_DBLSP_DRVSP
+ int res;
+#endif
+#ifdef DMSDOS_CONFIG_STAC
+ unsigned char mdfat_raw_field[5];
+#endif
+ Dblsb*dblsb=MSDOS_SB(sb)->private_data;
+
+ if(clusternr<2||clusternr>dblsb->s_max_cluster2)
+ { printk(KERN_ERR "DMSDOS: illegal mdfat access (cluster=%d max_cluster2=%d)\n",
+ clusternr,dblsb->s_max_cluster2);
+ goto fake_mde;
+ }
+
+ switch(dblsb->s_cvf_version)
+ {
+#ifdef DMSDOS_CONFIG_STAC
+ case STAC3:
+ case STAC4:
+ if(dblsb->s_16bitfat)pos=clusternr*4;
+ else pos=clusternr*3;
+ area=pos/SECTOR_SIZE;
+ offset=pos%SECTOR_SIZE;
+ area=(area/6)*9+(area%6)+3+dblsb->s_fatstart; /* yes!!! */
+ lock_mdfat();
+ merk_i=acache_get(sb,mdfat,area,-1,MDFATCACHESIZE);
+ if(merk_i<0)goto mdfat_error;
+ nr_of_bytes=3;
+ if(dblsb->s_16bitfat)nr_of_bytes=4;
+
+ /* read 2nd sector if necessary */
+ if(offset+nr_of_bytes-1>511)
+ { merk_i2=acache_get(sb,mdfat,area+1,merk_i,MDFATCACHESIZE);
+ if(merk_i2<0){++area;goto mdfat_error;}
+ }
+ else merk_i2=merk_i;
+
+ /* copy data in mdfat_raw_field (nr_of_bytes byte) */
+ mdfat_raw_field[3]=0; /* in case only 3 bytes to read */
+ for(i=0;i<nr_of_bytes;++i)
+ { if(i+offset<512)
+ mdfat_raw_field[i]=mdfat[merk_i].a_buffer->b_data[i+offset];
+ else
+ mdfat_raw_field[i]=mdfat[merk_i2].a_buffer->b_data[i+offset-512];
+ }
+
+ /* setup mde */
+ mde->sector_minus_1=CHS(mdfat_raw_field)+((mdfat_raw_field[3]&0x3f)<<16)-1;
+ mde->unknown=(mdfat_raw_field[2])&0xf0; /* same as flags here */
+ mde->size_lo_minus_1=(mdfat_raw_field[2]&0xf)+((mdfat_raw_field[3]>>2)&0x30);
+ mde->size_hi_minus_1=mde->size_lo_minus_1; /* for compatibility */
+ mde->flags=(mdfat_raw_field[2])&0xf0; /* unshifted like in sd4_c.cc */
+ /* set used and compressed bits in flags (for compatibility) */
+ /* Hi Pavel: Is there a bug here? sector seems to be sometimes 1 for
+ empty stacker mdfat slots. I think it should be 0 ? This showed up in
+ the new fs checking code as dead sectors. Hmm.... Should we tolerate
+ 0 and 1 here ? But then the stacker fs check complains later... */
+ /* Hi Frank, they are normal deleted clusters, stored for DOS undel.
+ They should be deleted automaticaly, when free space becomes low.
+ At this moment is best to ignore them in your fat simple check,
+ they are counted by stac simple check */
+ if(mde->sector_minus_1+1)mde->flags|=2; /*set 'used' flag*/
+ switch(mde->flags&0xa0)
+ { case 0x80:
+ case 0x00:
+ if(mde->size_lo_minus_1+1==dblsb->s_sectperclust)
+ mde->flags|=1;
+ break;
+ case 0x20:
+ mde->flags|=1;
+ break;
+ default:
+ /* correct length for compatibility */
+ mde->size_hi_minus_1=dblsb->s_sectperclust-1;
+ }
+
+ LOG_MDFAT("DMSDOS: dbl_mdfat_value: cluster %u\n",
+ (unsigned)clusternr);
+ LOG_MDFAT(" sector %u len %u flags 0x%X raw 0x%02X 0x%02X 0x%02X 0x%02X\n",
+ (unsigned)(mde->sector_minus_1+1),(unsigned)(mde->size_lo_minus_1+1),
+ (unsigned)mde->flags,
+ (unsigned)(mdfat_raw_field[0]),(unsigned)(mdfat_raw_field[1]),
+ (unsigned)(mdfat_raw_field[2]),(unsigned)(mdfat_raw_field[3]));
+ LOG_MDFAT(" pos %u area %u offset %u\n",
+ (unsigned)pos,(unsigned)area,(unsigned)offset);
+
+ /* if new!=NULL, setup nr_of_bytes byte from new in mdfat_raw_field */
+ if(new)
+ { mdfat_raw_field[0]=(new->sector_minus_1+1);
+ /* unknown bits ignored */
+ mdfat_raw_field[1]=(new->sector_minus_1+1)>>8;
+ mdfat_raw_field[3]=((new->sector_minus_1+1)>>16)&0x3f;
+ mdfat_raw_field[3]|=(new->size_lo_minus_1<<2)&0xc0;
+ mdfat_raw_field[2]=new->size_lo_minus_1&0x0f;
+ mdfat_raw_field[2]|=new->flags&0xf0; /* unshifted like in sd4_c.cc */
+
+ /* write back mdfat_raw_entry */
+ for(i=0;i<nr_of_bytes;++i)
+ { if(i+offset<512)
+ mdfat[merk_i].a_buffer->b_data[i+offset]=mdfat_raw_field[i];
+ else
+ mdfat[merk_i2].a_buffer->b_data[i+offset-512]=mdfat_raw_field[i];
+ }
+
+ /* mark buffer dirty (both if necessary) */
+ raw_mark_buffer_dirty(sb,mdfat[merk_i].a_buffer,1);
+ if(merk_i!=merk_i2)
+ raw_mark_buffer_dirty(sb,mdfat[merk_i2].a_buffer,1);
+ /* write second mdfat if it exists */
+ if(dblsb->s_2nd_fat_offset)
+ { struct buffer_head* bh;
+
+ bh=raw_getblk(sb,
+ mdfat[merk_i].a_area+dblsb->s_2nd_fat_offset);
+ if(bh==NULL)
+ { printk(KERN_ERR "DMSDOS: unable to read second mdfat\n");
+ goto give_up;
+ }
+ memcpy(bh->b_data,mdfat[merk_i].a_buffer->b_data,SECTOR_SIZE);
+ raw_set_uptodate(sb,bh,1);
+ raw_mark_buffer_dirty(sb,bh,1);
+ raw_brelse(sb,bh);
+ if(merk_i!=merk_i2)
+ { bh=raw_getblk(sb,
+ mdfat[merk_i2].a_area+dblsb->s_2nd_fat_offset);
+ if(bh==NULL)
+ { printk(KERN_ERR "DMSDOS: unable to read second mdfat\n");
+ goto give_up;
+ }
+ memcpy(bh->b_data,mdfat[merk_i2].a_buffer->b_data,SECTOR_SIZE);
+ raw_set_uptodate(sb,bh,1);
+ raw_mark_buffer_dirty(sb,bh,1);
+ raw_brelse(sb,bh);
+ }
+ }
+ give_up: ;
+ }
+
+ unlock_mdfat();
+ return 0;
+#endif
+#ifdef DMSDOS_CONFIG_DBLSP_DRVSP
+ case DBLSP:
+ case DRVSP:
+ pos=(dblsb->s_dcluster+clusternr)*4+512*dblsb->s_mdfatstart;
+ area=pos/SECTOR_SIZE;
+ offset=(pos%SECTOR_SIZE);
+ lock_mdfat();
+ merk_i=acache_get(sb,mdfat,area,-1,MDFATCACHESIZE);
+ if(merk_i<0)goto mdfat_error;
+ pp=&(mdfat[merk_i].a_buffer->b_data[offset]);
+ res=CHL(pp);
+ mde->sector_minus_1=res&0x1fffff;
+ mde->unknown=(res&0x00200000)>>21;
+ mde->size_lo_minus_1=(res&0x03c00000)>>22;
+ mde->size_hi_minus_1=(res&0x3c000000)>>26;
+ mde->flags=((res&0xC0000000)>>30)&3;
+ if(new)
+ { res=new->sector_minus_1;res&=0x1fffff;
+ /* unknown bit ??? don't know... set to zero */
+ res|=new->size_lo_minus_1<<22;res&=0x03ffffff;
+ res|=new->size_hi_minus_1<<26;res&=0x3fffffff;
+ res|=new->flags<<30;
+ pp[0]=res;pp[1]=res>>8;pp[2]=res>>16;pp[3]=res>>24;
+ raw_mark_buffer_dirty(sb,mdfat[merk_i].a_buffer,1);
+ }
+ unlock_mdfat();
+ return 0;
+#endif
+#ifdef DMSDOS_CONFIG_DRVSP3
+ case DRVSP3:
+ pos=(dblsb->s_dcluster+clusternr)*5
+ +((dblsb->s_dcluster+clusternr)/102)*2
+ +512*dblsb->s_mdfatstart;
+ area=pos/SECTOR_SIZE;
+ offset=(pos%SECTOR_SIZE);
+ lock_mdfat();
+ merk_i=acache_get(sb,mdfat,area,-1,MDFATCACHESIZE);
+ if(merk_i<0)goto mdfat_error;
+ pp=&(mdfat[merk_i].a_buffer->b_data[offset]);
+
+ /* setup mde */
+ mde->sector_minus_1=CHL(pp)&0xffffff;
+ mde->unknown=(CHL(pp)&0x3000000)>>24;
+ mde->size_lo_minus_1=(pp[3]>>2)&0x3f;
+ mde->size_hi_minus_1=pp[4]&0x3f;
+ mde->flags=(pp[4]>>6)&3;
+
+ /* if new!=NULL, setup 5 byte from new in pp */
+ if(new)
+ { pp[0]=new->sector_minus_1/*&0xffffff ??? */;
+ pp[1]=new->sector_minus_1>>8;
+ pp[2]=new->sector_minus_1>>16;
+ /*pp[3]=(new->sector_minus_1>>24)&3; ??? */
+ pp[3]=new->unknown&3; /* we need the fragmented bit here :) */
+ pp[3]|=new->size_lo_minus_1<<2;
+ pp[4]=new->size_hi_minus_1&0x3f;
+ pp[4]|=new->flags<<6;
+
+ raw_mark_buffer_dirty(sb,mdfat[merk_i].a_buffer,1);
+ }
+
+ unlock_mdfat();
+ return 0;
+#endif
+ } /* end switch(dblsb->s_cvf_version) */
+
+ printk(KERN_ERR "DMSDOS: dbl_mdfat_value: unknown version?? This is a bug.\n");
+ goto fake_mde;
+
+ mdfat_error:
+ unlock_mdfat();
+ printk(KERN_ERR "DMSDOS: unable to read mdfat area %d for cluster %d\n",area,
+ clusternr);
+
+ fake_mde:
+ mde->sector_minus_1=0;
+ mde->unknown=0;
+ mde->size_lo_minus_1=0;
+ mde->size_hi_minus_1=0;
+ mde->flags=0;
+
+ return -1;
+}
+
+int dbl_mdfat_cluster2sector(struct super_block*sb,int clusternr)
+{ Mdfat_entry mde;
+ Dblsb*dblsb=MSDOS_SB(sb)->private_data;
+
+ if(clusternr==0)return dblsb->s_rootdir;
+ if(dbl_mdfat_value(sb,clusternr,NULL,&mde)==0)
+ return mde.sector_minus_1+1;
+ return -1;
+}
+
+int dbl_fat_nextcluster(struct super_block*sb,int clusternr,int*new)
+{ int area;
+ int pos;
+ int offset;
+ int merk_i;
+ int res;
+ int newval;
+ int merk_i2;
+ int offset2;
+ Dblsb*dblsb=MSDOS_SB(sb)->private_data;
+
+ if(clusternr<2||clusternr>dblsb->s_max_cluster2)
+ { printk(KERN_ERR "DMSDOS: illegal dfat access (cluster=%d max_cluster2=%d)\n",
+ clusternr,dblsb->s_max_cluster2);
+ return -1;
+ }
+
+ pos= dblsb->s_16bitfat ? clusternr<<1/**2*/ : (clusternr*3)>>1/*/2*/;
+ area=pos>>SECTOR_BITS/*pos/SECTOR_SIZE*/;
+ offset=pos&(SECTOR_SIZE-1)/*pos%SECTOR_SIZE*/;
+
+ /* adapt area for stacker */
+ if(dblsb->s_cvf_version>=STAC3)area=(area/3)*9+(area%3);
+
+ area+=dblsb->s_fatstart;
+
+ lock_dfat();
+
+ merk_i=acache_get(sb,dfat,area,-1,DFATCACHESIZE);
+ if(merk_i<0)
+ {
+ dfat_error:
+ unlock_dfat();
+ printk(KERN_ERR "DMSDOS: unable to read dfat area %d for cluster %d\n",area,
+ clusternr);
+ return -1;
+ }
+ if(offset==511)
+ { /* overlap, must read following sector also */
+ merk_i2=acache_get(sb,dfat,area+1,merk_i,DFATCACHESIZE);
+ if(merk_i2<0){++area;goto dfat_error;}
+ offset2=0;
+ }
+ else
+ { /* normal */
+ merk_i2=merk_i;
+ offset2=offset+1;
+ }
+
+LOG_DFAT("DMSDOS: FAT lookup: area=%d merk_i=%d merk_i2=%d offset=%d offset2=%d\n",
+ area,merk_i,merk_i2,offset,offset2);
+LOG_DFAT("DMSDOS: FAT lookup: cluster=%d value(low=%d high=%d)\n",
+ clusternr,
+ dfat[merk_i].a_buffer->b_data[offset],
+ dfat[merk_i2].a_buffer->b_data[offset2]);
+
+ res=dfat[merk_i].a_buffer->b_data[offset];
+ res&=0xff; /* grmpf... sign problems !!!! this is brutal but it works */
+ res|=dfat[merk_i2].a_buffer->b_data[offset2]<<8;
+ res&=0xffff;
+
+ if(new)
+ { if(dblsb->s_16bitfat) newval=*new&0xFFFF;
+ else
+ { if(clusternr&1) newval=(res&0xF) | ((*new&0xFFF)<<4);
+ else newval=(res&0xF000) | (*new&0xFFF);
+ }
+ dfat[merk_i].a_buffer->b_data[offset]=newval;
+ dfat[merk_i2].a_buffer->b_data[offset2]=newval>>8;
+ raw_mark_buffer_dirty(sb,dfat[merk_i].a_buffer,1);
+ if(merk_i!=merk_i2)
+ raw_mark_buffer_dirty(sb,dfat[merk_i2].a_buffer,1);
+ /* write second fat if it exists */
+ if(dblsb->s_2nd_fat_offset)
+ { struct buffer_head* bh;
+
+ bh=raw_getblk(sb,
+ dfat[merk_i].a_area+dblsb->s_2nd_fat_offset);
+ if(bh==NULL)
+ { printk(KERN_ERR "DMSDOS: unable to read second dfat\n");
+ goto give_up;
+ }
+ memcpy(bh->b_data,dfat[merk_i].a_buffer->b_data,SECTOR_SIZE);
+ raw_set_uptodate(sb,bh,1);
+ raw_mark_buffer_dirty(sb,bh,1);
+ raw_brelse(sb,bh);
+ if(merk_i!=merk_i2)
+ { bh=raw_getblk(sb,
+ dfat[merk_i2].a_area+dblsb->s_2nd_fat_offset);
+ if(bh==NULL)
+ { printk(KERN_ERR "DMSDOS: unable to read second dfat\n");
+ goto give_up;
+ }
+ memcpy(bh->b_data,dfat[merk_i2].a_buffer->b_data,SECTOR_SIZE);
+ raw_set_uptodate(sb,bh,1);
+ raw_mark_buffer_dirty(sb,bh,1);
+ raw_brelse(sb,bh);
+ }
+ }
+ give_up: ;
+ }
+ unlock_dfat();
+ if(dblsb->s_16bitfat)return res>=0xFFF7 ? -1 : res;
+ if(clusternr&1)res>>=4; else res&=0xfff;
+ return res>=0xff7 ? -1 : res;
+}
+
+int dbl_bitfat_value(struct super_block*sb,int sectornr,int*new)
+{ int area;
+ int pos;
+ int offset;
+ int merk_i;
+#ifdef DMSDOS_CONFIG_DBL
+ unsigned char * pp;
+ int newval;
+#endif
+ int bitmask;
+#ifdef DMSDOS_CONFIG_STAC
+ int shiftval;
+#endif
+ int res;
+ Dblsb*dblsb=MSDOS_SB(sb)->private_data;
+
+ if(sectornr<dblsb->s_datastart)return -1;
+ if(sectornr>dblsb->s_dataend)return -1;
+
+ switch(dblsb->s_cvf_version)
+ {
+#ifdef DMSDOS_CONFIG_STAC3
+ case STAC3:
+ pos=((sectornr-dblsb->s_datastart)>>3);
+ offset=pos&(SECTOR_SIZE-1)/*pos%SECTOR_SIZE*/;
+ area=(pos>>SECTOR_BITS)/*pos/SECTOR_SIZE*/+dblsb->s_mdfatstart; /* misused */
+ shiftval=((sectornr-dblsb->s_datastart)&7);
+ bitmask=0x1;
+ lock_bitfat();
+ merk_i=acache_get(sb,bitfat,area,-1,BITFATCACHESIZE);
+ if(merk_i<0)goto bitfat_error;
+ res=(bitfat[merk_i].a_buffer->b_data[offset]>>shiftval)&bitmask;
+ if(new)
+ { if(dblsb->s_free_sectors>=0&&res!=0&&*new==0)++dblsb->s_free_sectors;
+ if(dblsb->s_free_sectors>=0&&res==0&&*new!=0)--dblsb->s_free_sectors;
+ bitfat[merk_i].a_buffer->b_data[offset]&=~(bitmask<<shiftval);
+ bitfat[merk_i].a_buffer->b_data[offset]|=(*new&bitmask)<<shiftval;
+ raw_mark_buffer_dirty(sb,bitfat[merk_i].a_buffer,1);
+ }
+ unlock_bitfat();
+ return res;
+#endif
+#ifdef DMSDOS_CONFIG_STAC4
+ case STAC4:
+ pos=((sectornr-dblsb->s_datastart)>>2);
+ offset=pos&(SECTOR_SIZE-1)/*pos%SECTOR_SIZE*/;
+ area=(pos>>SECTOR_BITS)/*pos/SECTOR_SIZE*/+dblsb->s_mdfatstart; /* misused */
+ shiftval=((sectornr-dblsb->s_datastart)&3)<<1/**2*/;
+ bitmask=0x3;
+ lock_bitfat();
+ merk_i=acache_get(sb,bitfat,area,-1,BITFATCACHESIZE);
+ if(merk_i<0)goto bitfat_error;
+ res=(bitfat[merk_i].a_buffer->b_data[offset]>>shiftval)&bitmask;
+ if(new)
+ { if(dblsb->s_free_sectors>=0&&res!=0&&*new==0)++dblsb->s_free_sectors;
+ if(dblsb->s_free_sectors>=0&&res==0&&*new!=0)--dblsb->s_free_sectors;
+ bitfat[merk_i].a_buffer->b_data[offset]&=~(bitmask<<shiftval);
+ bitfat[merk_i].a_buffer->b_data[offset]|=(*new&bitmask)<<shiftval;
+ raw_mark_buffer_dirty(sb,bitfat[merk_i].a_buffer,1);
+ }
+ unlock_bitfat();
+ return res;
+#endif
+#ifdef DMSDOS_CONFIG_DBL
+ case DBLSP:
+ case DRVSP:
+ case DRVSP3:
+ pos=SECTOR_SIZE+((sectornr-dblsb->s_datastart)>>4)*2;
+ area=pos>>SECTOR_BITS/*pos/SECTOR_SIZE*/;
+ offset=pos&(SECTOR_SIZE-1)/*(pos%SECTOR_SIZE)*/;
+ bitmask=0x8000>>((sectornr-dblsb->s_datastart)&15);
+ lock_bitfat();
+ merk_i=acache_get(sb,bitfat,area,-1,BITFATCACHESIZE);
+ if(merk_i<0)goto bitfat_error;
+ pp=&(bitfat[merk_i].a_buffer->b_data[offset]);
+ res=CHS(pp);
+ if(new)
+ { newval= (*new) ? (res|bitmask) : (res&~bitmask);
+ pp[0]=newval;pp[1]=newval>>8;
+ raw_mark_buffer_dirty(sb,bitfat[merk_i].a_buffer,1);
+ }
+ res=(res&bitmask) ? 1 : 0;
+ if(new)
+ { /* WARNING: variable res is different from above */
+ if(dblsb->s_free_sectors>=0&&res!=0&&*new==0)++dblsb->s_free_sectors;
+ if(dblsb->s_free_sectors>=0&&res==0&&*new!=0)--dblsb->s_free_sectors;
+ }
+ unlock_bitfat();
+ return res;
+#endif
+ }
+
+ printk(KERN_ERR "DMSDOS: dbl_bitfat_value: version not found?? cannot happen\n");
+ return -1;
+
+ bitfat_error:
+ unlock_bitfat();
+ printk(KERN_ERR "DMSDOS: unable to read bitfat area %d for sector %d\n",area,
+ sectornr);
+ return -1;
+}
+
+#ifndef __DMSDOS_LIB__
+int dblspace_fat_access(struct super_block*sb, int clusternr, int newval)
+{ int cl;
+
+ cl=dbl_fat_nextcluster(sb,clusternr,NULL);
+
+ if(newval==-1) return cl;
+
+ if(sb->s_flags&MS_RDONLY)
+ { printk(KERN_ERR "DMSDOS: dblspace_fat_access: READ-ONLY filesystem\n");
+ /* This is a bad hack in order to work around a problem with the
+ FAT driver: The FAT driver assumes fat_access never fails. Thus
+ returning -EROFS results in an endless loop (i.e. system hang)
+ at least in fat_free. We return -1 here in order to simulate EOF
+ which should break any loop in the FAT driver. */
+ return /* -EROFS */ -1;
+ }
+
+ if(newval==0)delete_cache_cluster(sb,clusternr);
+ dbl_fat_nextcluster(sb,clusternr,&newval);
+ if(cl<0)return -1; /* see comment above -- just to be sure :) */
+ /* if cl _is_ -1 (EOF) this is ok. */
+ /* if it is a negative error it is replaced by EOF. */
+ return cl;
+}
+
+int dblspace_bmap(struct inode*inode, int block)
+{
+ #ifdef __FOR_KERNEL_2_3_10
+ return dblspace_smap(inode,block);
+ #else
+ printk(KERN_WARNING "DMSDOS: bmap called, unsupported!\n");
+ return -EIO;
+ #endif
+}
+
+int dblspace_smap(struct inode*inode, int block)
+{ int cluster;
+ int sect_offs;
+ Dblsb*dblsb=MSDOS_SB(inode->i_sb)->private_data;
+
+ cluster=block/dblsb->s_sectperclust;
+ sect_offs=block%dblsb->s_sectperclust;
+
+ LOG_FS("DMSDOS: smap called, block=%d cluster_offs=%d sector_offs=%d\n",
+ block,cluster,sect_offs);
+
+ if (inode->i_ino == MSDOS_ROOT_INO || (S_ISDIR(inode->i_mode) &&
+ !MSDOS_I(inode)->i_start))
+ {
+ if (block >= MSDOS_SB(inode->i_sb)->dir_entries >> MSDOS_DPS_BITS)
+ { LOG_FS("DMSDOS: smap: root dir beyond end, returning 0\n");
+ return 0;
+ }
+ LOG_FS("DMSDOS: smap: root dir, returning %d\n",block+FAKED_ROOT_DIR_OFFSET);
+ return block+FAKED_ROOT_DIR_OFFSET;
+ }
+
+ if (!(cluster = get_cluster(inode,cluster)))
+ { LOG_FS("DMSDOS: smap: get_cluster returned 0\n");
+ return 0;
+ }
+ LOG_FS("DMSDOS: smap: returning vsector(cluster=%d sector=%d)\n",
+ cluster,sect_offs);
+ return ((cluster-2)*dblsb->s_sectperclust)+sect_offs+FAKED_DATA_START_OFFSET;
+}
+
+/* clusternr is absolute, not relative to inode */
+void dblspace_zero_new_cluster(struct inode*inode, int clusternr)
+{ /* we decide upon the inode whether this is a dir cluster */
+ struct super_block*sb=inode->i_sb;
+ Dblsb*dblsb=MSDOS_SB(sb)->private_data;
+ Cluster_head*ch;
+ int i;
+
+ if(!S_ISDIR(inode->i_mode))
+ { /* we just throw the cluster away if it has an mdfat entry */
+ delete_cache_cluster(sb,clusternr);
+ }
+ else
+ { /* it may be in cache, so ... */
+ ch=ch_read(sb,clusternr,C_NO_READ|C_KEEP_LOCK); /* I hope that noread is OK there, Pavel */
+ if(ch==NULL)
+ { printk(KERN_ERR "DMSDOS: zero_new_cluster: ch_noread failed???\n");
+ return;
+ }
+ memset(ch->c_data,0,dblsb->s_sectperclust*SECTOR_SIZE);
+ if(DIR_MAY_BE_SHORT(dblsb))ch->c_length=SECTOR_SIZE;
+ else ch->c_length=dblsb->s_sectperclust*SECTOR_SIZE;
+ /* ch_dirty_locked unlocks the cluster *after* write */
+ i=ch_dirty_locked(ch,0,
+ DIR_MAY_BE_COMPRESSED(dblsb)?UC_NORMAL:UC_UNCOMPR);
+ if(i<0&&i!=-EROFS) /* don't try until death on a read-only filesystem */
+ ch_dirty_retry_until_success(ch,0,
+ DIR_MAY_BE_COMPRESSED(dblsb)?UC_NORMAL:UC_UNCOMPR);
+ ch_free(ch);
+ }
+}
+#endif
diff --git a/src/dblspace_virtual.c b/src/dblspace_virtual.c
new file mode 100644
index 0000000..7c386bd
--- /dev/null
+++ b/src/dblspace_virtual.c
@@ -0,0 +1,989 @@
+/*
+dblspace_virtual.c
+
+DMSDOS CVF-FAT module: virtual buffer 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.
+******************************************************************************
+
+*/
+
+#ifndef __KERNEL__
+#error This file needs __KERNEL__
+#endif
+
+#include <linux/fs.h>
+#include <linux/msdos_fs.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/stat.h>
+#include <linux/mm.h>
+#include <linux/malloc.h>
+#include <asm/semaphore.h>
+#include "dmsdos.h"
+
+/* Here we do a virtual sector translation */
+
+#define FAKED_BH_MAGIC 0xfff0
+/* i hope this never occurs in kernel buffers... */
+
+extern unsigned long int dmsdos_speedup;
+
+/* cluster caching ... */
+Cluster_head ccache[CCACHESIZE];
+
+DECLARE_MUTEX(ccache_sem); /* Must be initialized to green light */
+void lock_ccache(void) {down(&ccache_sem);}
+void unlock_ccache(void) {up(&ccache_sem);}
+
+DECLARE_WAIT_QUEUE_HEAD(fullwait);
+
+int help_ccache=0;
+
+/* must be called ccache locked */
+/* function does not deal with locking */
+Cluster_head* find_in_ccache(struct super_block*sb,
+ int clusternr,Cluster_head**lastfree,
+ Cluster_head**oldest)
+{ int i;
+ int lastfree_n=-1;
+ int oldest_n=-1;
+ unsigned int oldest_time=CURRENT_TIME;
+
+ for(i=0;i<CCACHESIZE;++i)
+ { if(ccache[i].c_flags==C_FREE)lastfree_n=i;
+ else
+ { if(ccache[i].c_time<oldest_time&&ccache[i].c_count==0)
+ { oldest_n=i;
+ oldest_time=ccache[i].c_time;
+ }
+ if(ccache[i].c_clusternr==clusternr&&ccache[i].c_sb->s_dev==sb->s_dev)
+ { /* found */
+ return &(ccache[i]);
+ }
+ }
+ }
+ if(lastfree_n>=0&&lastfree!=NULL)*lastfree=&(ccache[lastfree_n]);
+ /* uhhh, deadlock possible here... we should always have an oldest when
+ there's nothing free any more... */
+ /* use help_ccache to avoid always choosing the same cluster */
+ if(lastfree_n<0&&oldest_n<0&&oldest!=NULL)
+ { /* search more carefully */
+ /* candidates are all those with count==0 */
+ for(i=0;i<CCACHESIZE;++i)
+ { ++help_ccache; if(help_ccache>=CCACHESIZE)help_ccache=0;
+ if(ccache[help_ccache].c_count==0)
+ { oldest_n=help_ccache;
+ break;
+ }
+ }
+ }
+ if(oldest_n>=0&&oldest!=NULL)*oldest=&(ccache[oldest_n]);
+ return NULL;
+}
+
+int count_error_clusters(void)
+{ int i;
+ int errs=0;
+
+ for(i=0;i<CCACHESIZE;++i)
+ { if(ccache[i].c_flags==C_DIRTY_NORMAL||ccache[i].c_flags==C_DIRTY_UNCOMPR)
+ if(ccache[i].c_errors)++errs;
+ }
+ if(errs)printk("DMSDOS: %d error clusters waiting in cache\n",errs);
+ return errs;
+}
+
+/* this is called if a cluster write fails too often */
+/* the cluster is expected to be locked */
+#define MAX_ERRORS 5
+#define MAX_ERROR_CLUSTERS ((CCACHESIZE/10)+1)
+void handle_error_cluster(Cluster_head*ch)
+{ Dblsb*dblsb;
+
+ if(ch->c_errors>MAX_ERRORS||count_error_clusters()>MAX_ERROR_CLUSTERS)
+ { /* get rid of the cluster to prevent dmsdos crash due to endless loops */
+ /* this shouldn't hurt much since the filesystem is already damaged */
+ printk(KERN_CRIT "DMSDOS: dirty cluster %d on dev 0x%x removed, data are lost\n",
+ ch->c_clusternr,ch->c_sb->s_dev);
+ /*dmsdos_write_cluster(sb,NULL,0,clusternr,0,0); better not do this*/
+ ch->c_flags=C_DELETED;
+ ch->c_clusternr=-1;
+ if((ch->c_sb->s_flags&MS_RDONLY)==0)
+ { printk(KERN_CRIT "DMSDOS: filesystem on dev 0x%x probably damaged, set to READ-ONLY mode\n",
+ ch->c_sb->s_dev);
+ ch->c_sb->s_flags|=MS_RDONLY;
+ dblsb=MSDOS_SB(ch->c_sb)->private_data;
+ dblsb->s_comp=READ_ONLY;
+ }
+ }
+ else
+ { printk(KERN_CRIT "DMSDOS: cannot write dirty cluster %d on dev 0x%x c_error=%d, trying again later\n",
+ ch->c_clusternr,ch->c_sb->s_dev,ch->c_errors);
+ }
+}
+
+void lock_ch(Cluster_head*ch)
+{ if(ch->c_count==0)panic("DMSDOS: lock_ch: count=0! This is a bug.\n");
+ down(&(ch->c_sem));
+}
+
+void unlock_ch(Cluster_head*ch){up(&(ch->c_sem));}
+
+void dump_ccache(void)
+{ int i;
+
+ printk(KERN_INFO "DMSDOS: ccache contents:\n");
+ lock_ccache();
+ for(i=0;i<CCACHESIZE;++i)
+ { printk(KERN_INFO "slot %d: time=0x%x flags=",i,ccache[i].c_time);
+ switch(ccache[i].c_flags)
+ { case C_FREE: printk("FREE"); break;
+ case C_VALID: printk("VALID"); break;
+ case C_INVALID: printk("INVALID"); break;
+ case C_DIRTY_NORMAL: printk("DIRTY_NORMAL"); break;
+ case C_DIRTY_UNCOMPR: printk("DIRTY_UNCOMPR"); break;
+ case C_DELETED: printk("DELETED"); break;
+ case C_NOT_MALLOCD: printk("NOT_MALLOCD"); break;
+ default: printk("unknown(?)");
+ }
+ printk(" count=%d length=%d clusternr=%d dev=0x%x errors=%d\n",
+ ccache[i].c_count,ccache[i].c_length,ccache[i].c_clusternr,
+ /* note that c_sb of a free slot may point to anywhere, so... */
+ ccache[i].c_flags!=C_FREE?ccache[i].c_sb->s_dev:0,
+ ccache[i].c_errors);
+ }
+ unlock_ccache();
+}
+
+/* get a (probably empty) ccache slot for cluster */
+/* this function does not malloc memory or read the data */
+/* the returned cluster is locked */
+#define MAX_RETRIES 100 /* should just break the loop in case of ... */
+Cluster_head* get_ch(struct super_block*sb,int clusternr)
+{ Cluster_head*free=NULL;
+ Cluster_head*oldest=NULL;
+ Cluster_head*actual;
+ int count_retries=0;
+ struct super_block*sb2;
+
+ retry:
+ if(count_retries++>MAX_RETRIES)
+ { printk(KERN_WARNING "DMSDOS: get_ch: max_retries reached, breaking loop. This may be a bug.\n");
+ return NULL;
+ }
+
+ lock_ccache();
+
+ actual=find_in_ccache(sb,clusternr,&free,&oldest);
+
+ /* the simplest case: we have found it in cache */
+ if(actual)
+ { ++(actual->c_count);
+ actual->c_time=CURRENT_TIME;
+ unlock_ccache();
+ lock_ch(actual);
+ if(actual->c_sb!=sb||actual->c_clusternr!=clusternr)
+ { /* looks like some other process was faster and discarded it :( */
+ printk(KERN_INFO "DMSDOS: get_ch: actual looks modified ARGHHH, retrying\n");
+ unlock_ch(actual);
+ lock_ccache();
+ --(actual->c_count);
+ unlock_ccache();
+ goto retry;
+ }
+ return actual;
+ }
+
+ /* we have not found it, instead we found a free slot */
+ if(free)
+ { if(free->c_count!=0)
+ { printk(KERN_WARNING "DMSDOS: get_ch: free->c_count!=0\n");
+ }
+ free->c_count=1;
+ free->c_flags=C_NOT_MALLOCD;
+ free->c_sb=sb;
+ free->c_clusternr=clusternr;
+ free->c_time=CURRENT_TIME;
+ free->c_errors=0;
+ unlock_ccache();
+ lock_ch(free);
+ if(free->c_sb!=sb||free->c_clusternr!=clusternr)
+ { /* looks like some other process was faster and discarded it :( */
+ printk(KERN_INFO "DMSDOS: get_ch: free looks modified ARGHHH, retrying\n");
+ unlock_ch(free);
+ lock_ccache();
+ --(free->c_count);
+ unlock_ccache();
+ goto retry;
+ }
+ return free;
+ }
+
+ /* now the most complex case: we must discard one cluster */
+ if(oldest)
+ { if(oldest->c_count!=0)
+ { printk(KERN_WARNING "DMSDOS: get_ch: oldest->c_count!=0\n");
+ }
+ oldest->c_count=1;
+ /* is this the time touch really necessary here ? */
+ /*oldest->c_time=CURRENT_TIME;*/
+ unlock_ccache();
+ lock_ch(oldest);
+ switch(oldest->c_flags)
+ { case C_DIRTY_NORMAL:
+ case C_DIRTY_UNCOMPR:
+ sb2=oldest->c_sb;
+ if(dmsdos_write_cluster(sb2,oldest->c_data,oldest->c_length,
+ oldest->c_clusternr,0,
+ oldest->c_flags==C_DIRTY_NORMAL?UC_NORMAL:UC_UNCOMPR)<0)
+ { /* write failed */
+ oldest->c_time=CURRENT_TIME;
+ ++(oldest->c_errors);
+ handle_error_cluster(oldest);
+ break;
+ }
+ /* successfully written */
+ oldest->c_flags=C_VALID;
+ oldest->c_errors=0;
+ /* fall through */
+ case C_VALID:
+ case C_INVALID:
+ if(oldest->c_count>1)break; /* we cannot do anything here */
+ /* we are the only process using it */
+ FREE(oldest->c_data);
+ oldest->c_flags=C_NOT_MALLOCD;
+ /* fall through */
+ case C_NOT_MALLOCD:
+ if(oldest->c_count>1)break;
+ /* now we have a free slot */
+ oldest->c_sb=sb;
+ oldest->c_clusternr=clusternr;
+ oldest->c_time=CURRENT_TIME;
+ oldest->c_errors=0;
+ return oldest;
+ }
+ unlock_ch(oldest);
+ lock_ccache();
+ --(oldest->c_count);
+ unlock_ccache();
+ /* anything may have happened meanwhile */
+ goto retry;
+ }
+
+ /* the last case: not found, no one free and no one found to discard */
+ /* all are in use - we must wait */
+ unlock_ccache();
+ LOG_CCACHE("DMSDOS: cluster cache full, waiting...\n");
+ /* or may it be better to abort here ? */
+ sleep_on(&fullwait);
+ goto retry;
+
+ return NULL;
+}
+
+/* this routine *must* return a cluster with its slack zerod out if the
+ cluster doesn't have maximum length and flag C_NO_READ is unset */
+Cluster_head* ch_read(struct super_block*sb,int clusternr,int flag)
+{ Cluster_head*ch;
+ Dblsb*dblsb;
+ int i;
+
+ LOG_CCACHE("DMSDOS: ch_read cluster %d\n",clusternr);
+ ch=get_ch(sb,clusternr);
+ if(ch==NULL)return NULL;
+ /* get_ch returns a locked cluster slot */
+ LOG_CCACHE("DMSDOS: ch_read: got count=%d\n",ch->c_count);
+ dblsb=MSDOS_SB(ch->c_sb)->private_data;
+
+ /* check whether we need to get memory */
+ if(ch->c_flags==C_NOT_MALLOCD)
+ { ch->c_data=MALLOC(dblsb->s_sectperclust*SECTOR_SIZE);
+ if(ch->c_data==NULL)
+ { printk(KERN_ERR "DMSDOS: ch_read: no memory!\n");
+ unlock_ch(ch);
+ ch_free(ch);
+ return NULL;
+ }
+ else ch->c_flags=C_INVALID;
+ }
+
+ /* check whether we need to read the data from disk */
+ if(ch->c_flags==C_INVALID)
+ { ch->c_length=0;
+ if((flag&C_NO_READ)==0)
+ { i=dmsdos_read_cluster(sb,ch->c_data,clusternr);
+ if(i<0)
+ { printk(KERN_ERR "DMSDOS: ch_read: read_cluster failed\n");
+ unlock_ch(ch);
+ ch_free(ch);
+ return NULL;
+ }
+ ch->c_length=i;
+ }
+ ch->c_flags=C_VALID;
+ }
+
+ /* check whether the lock is to be kept */
+ if((flag&C_KEEP_LOCK)==0)unlock_ch(ch);
+
+ LOG_CCACHE("DMSDOS: ch_read finished.\n");
+ return ch;
+}
+
+int ch_dirty_locked(Cluster_head*ch, int near, int ucflag)
+{ /* Hmm...
+ We must ensure that we can safely delay writing of the cluster
+ before. This is done by calling the write_cluster function with
+ the SIMULATE flag. If there's a problem then it is expected to
+ return an error code (return value<0). In that case we fall back to
+ the old write-through caching mechanism.
+ */
+ /* IMPORTANT: This function expects that the cluster has already
+ been locked. If not, use ch_dirty intead.
+ */
+
+ int i;
+ struct super_block*sb=ch->c_sb;
+
+ if(sb->s_flags&MS_RDONLY)
+ { printk(KERN_ERR "DMSDOS: ch_dirty(_locked): READ-ONLY filesystem\n");
+ unlock_ch(ch);
+ return -EROFS; /*this might be bad, but...*/
+ }
+
+ LOG_CCACHE("DMSDOS: ch_dirty_locked cluster %d flags %d ucflag %d\n",
+ ch->c_clusternr,ch->c_flags,ucflag);
+
+ /* we expect the cluster to be already locked */
+ /* this is not checked here */
+
+ /* do not write free or deleted clusters or other junk */
+ if(ch->c_flags==C_FREE||ch->c_flags==C_DELETED||ch->c_flags==C_INVALID
+ ||ch->c_flags==C_NOT_MALLOCD)
+ { unlock_ch(ch);
+ return 0;
+ }
+
+ if(dmsdos_speedup&SP_USE_WRITE_BACK)
+ {
+ /* call write_cluster with SIMULATE flag (ucflag=UC_TEST) first */
+ i=dmsdos_write_cluster(sb,ch->c_data,ch->c_length,ch->c_clusternr,
+ near,UC_TEST);
+ if(i>=0)
+ { /* success - we can safely delay writing this cluster */
+ ch->c_flags=ucflag!=UC_NORMAL?C_DIRTY_UNCOMPR:C_DIRTY_NORMAL;
+ unlock_ch(ch);
+ return i;
+ }
+ /* otherwise fall back to the default write-through code */
+ }
+
+ i=dmsdos_write_cluster(sb,ch->c_data,ch->c_length,ch->c_clusternr,
+ near,ucflag);
+ /* the cluster is *not* marked dirty since failure is *reported* to the
+ calling process, which can call ch_dirty_retry_until_success if the
+ data really *must* be written */
+ /* so we also don't increment the error counter */
+ unlock_ch(ch);
+ return i;
+}
+
+int ch_dirty(Cluster_head*ch, int near, int ucflag)
+{ /* same as ch_dirty_locked, but cluster is expected not to be locked */
+ /* IMPORTANT: The cluster must not be locked by calling process as this
+ will cause a deadlock. If you have just locked the cluster, use
+ ch_dirty_locked instead.
+ */
+
+ LOG_CCACHE("DMSDOS: ch_dirty cluster %d flags %d ucflag %d\n",
+ ch->c_clusternr,ch->c_flags,ucflag);
+
+ lock_ch(ch);
+ /* ch_dirty_locked unlocks the cluster */
+ return ch_dirty_locked(ch,near,ucflag);
+}
+
+/* This routine is called if extremely important data (directory structure)
+ really must be written when normal ch_dirty already fails. However, there's
+ no guarantee that the data can really be written some time later. This
+ function is meant as a last resort. Code will discard the data later if too
+ many errors occur and set the filesystem to read-only mode. */
+void ch_dirty_retry_until_success(Cluster_head*ch, int near, int ucflag)
+{ lock_ch(ch);
+ if(ch->c_flags!=C_FREE&&ch->c_flags!=C_DELETED&&ch->c_flags!=C_INVALID
+ &&ch->c_flags!=C_NOT_MALLOCD)
+ ch->c_flags=ucflag!=UC_NORMAL?C_DIRTY_UNCOMPR:C_DIRTY_NORMAL;
+ unlock_ch(ch);
+}
+
+void ch_free(Cluster_head*ch)
+{ lock_ccache();
+ --(ch->c_count);
+ LOG_CCACHE("DMSDOS: ch_free cluster %d count_after_free %d\n",
+ ch->c_clusternr,ch->c_count);
+ if(ch->c_count==0)
+ { /* throw away unused deleted clusters immediately */
+ if(ch->c_flags==C_DELETED)
+ { FREE(ch->c_data);
+ ch->c_flags=C_FREE;
+ }
+ /* throw away unused and not malloc'd clusters also */
+ if(ch->c_flags==C_NOT_MALLOCD)ch->c_flags=C_FREE;
+
+ unlock_ccache();
+ wake_up(&fullwait);
+ return;
+ }
+ unlock_ccache();
+}
+
+void ccache_init()
+{ int i;
+
+ for(i=0;i<CCACHESIZE;++i)
+ { ccache[i].c_flags=C_FREE;
+ ccache[i].c_time=0;
+ ccache[i].c_count=0;
+ init_MUTEX(&ccache[i].c_sem);
+ ccache[i].c_errors=0;
+ }
+}
+
+/* for unmount */
+/* we assume that the clusters of the device to be unmounted are
+ - not in use (count==0) and
+ - not locked
+ either of them would mean that an inode of the filesystem is currently
+ in use and thus the filesystem driver shouldn't allow unmount...
+ ...violation will cause segfaults in a lot of other places...
+*/
+void free_ccache_dev(struct super_block*sb)
+{ int i;
+
+ retry:
+ lock_ccache();
+ for(i=0;i<CCACHESIZE;++i)
+ { if(ccache[i].c_flags!=C_FREE&&ccache[i].c_sb->s_dev==sb->s_dev)
+ { /* write it before if it is dirty... */
+ if(ccache[i].c_flags==C_DIRTY_NORMAL||ccache[i].c_flags==C_DIRTY_UNCOMPR)
+ { ++(ccache[i].c_count);
+ unlock_ccache();
+ lock_ch(&(ccache[i]));
+ if(ccache[i].c_flags==C_DIRTY_NORMAL||
+ ccache[i].c_flags==C_DIRTY_UNCOMPR)
+ { /* we do not check for failure since we cannot do anything for it
+ at unmount time (except panic, but this would be worse) */
+ dmsdos_write_cluster(ccache[i].c_sb,ccache[i].c_data,
+ ccache[i].c_length,ccache[i].c_clusternr,0,
+ ccache[i].c_flags==C_DIRTY_NORMAL?UMOUNT_UCFLAG:UC_UNCOMPR);
+ ccache[i].c_flags=C_VALID;
+ }
+ unlock_ch(&(ccache[i]));
+ lock_ccache();
+ --(ccache[i].c_count);
+ unlock_ccache();
+
+ /* I'm not sure whether we can be sure here, so just the safe way */
+ goto retry;
+ }
+
+ if(ccache[i].c_count)printk(KERN_ERR "DMSDOS: free_ccache_dev: oh oh, freeing busy cluster...\n");
+ if(ccache[i].c_flags!=C_NOT_MALLOCD)FREE(ccache[i].c_data);
+ ccache[i].c_flags=C_FREE;
+ wake_up(&fullwait);
+ }
+ }
+ unlock_ccache();
+}
+
+/* for memory management */
+/* We simply ignore clusters that are currently in use
+ (count!=0). This should be safe now. */
+void free_idle_ccache(void)
+{ int i;
+
+ retry:
+ lock_ccache();
+ for(i=0;i<CCACHESIZE;++i)
+ { if(ccache[i].c_flags!=C_FREE&&ccache[i].c_count==0) /*those with count=0
+ are never locked */
+ { /* hmm... would be a good idea to make it depend on free system
+ memory whether to discard which cached clusters... and how many...
+ any ideas? */
+ if(CURRENT_TIME-ccache[i].c_time>MAX_CCACHE_TIME)
+ { /* throw away */
+ /* but write it before if it is dirty... */
+ if(ccache[i].c_flags==C_DIRTY_NORMAL||ccache[i].c_flags==C_DIRTY_UNCOMPR)
+ { ++(ccache[i].c_count);
+ unlock_ccache();
+ lock_ch(&(ccache[i]));
+ if(ccache[i].c_flags==C_DIRTY_NORMAL||
+ ccache[i].c_flags==C_DIRTY_UNCOMPR)
+ { if(dmsdos_write_cluster(ccache[i].c_sb,ccache[i].c_data,
+ ccache[i].c_length,ccache[i].c_clusternr,0,
+ ccache[i].c_flags==C_DIRTY_NORMAL?UC_NORMAL:UC_UNCOMPR)>=0
+ )
+ { ccache[i].c_flags=C_VALID;
+ ccache[i].c_errors=0;
+ }
+ else /* failed */
+ { ccache[i].c_time=CURRENT_TIME; /* so we don't catch it again
+ in retry */
+ ++(ccache[i].c_errors);
+ handle_error_cluster(&(ccache[i]));
+ }
+ }
+ unlock_ch(&(ccache[i]));
+ lock_ccache();
+ --(ccache[i].c_count);
+ unlock_ccache();
+
+ /* I'm not sure whether we must restart here */
+ goto retry;
+ }
+ if(ccache[i].c_flags!=C_NOT_MALLOCD)FREE(ccache[i].c_data);
+ ccache[i].c_flags=C_FREE;
+ wake_up(&fullwait);
+ }
+ }
+ }
+ unlock_ccache();
+
+ /* wake up forgotten fullwaits - for safety */
+ wake_up(&fullwait);
+}
+
+/* for cluster cache syncing - writes all currently unused dirty clusters */
+void sync_cluster_cache(int allow_daemon)
+{ int i;
+ struct super_block*sb;
+
+ lock_ccache();
+
+ for(i=0;i<CCACHESIZE;++i)
+ { if(ccache[i].c_flags==C_DIRTY_NORMAL||ccache[i].c_flags==C_DIRTY_UNCOMPR)
+ { sb=ccache[i].c_sb;
+ ++(ccache[i].c_count);
+ unlock_ccache();
+ lock_ch(&(ccache[i]));
+ if(ccache[i].c_flags==C_DIRTY_NORMAL
+ ||ccache[i].c_flags==C_DIRTY_UNCOMPR)
+ { if(dmsdos_write_cluster(sb,ccache[i].c_data,ccache[i].c_length,
+ ccache[i].c_clusternr,0,
+ ccache[i].c_flags==C_DIRTY_UNCOMPR?UC_UNCOMPR:
+ (allow_daemon?UC_NORMAL:UC_DIRECT)
+ ) >=0
+ )
+ { ccache[i].c_flags=C_VALID;
+ ccache[i].c_errors=0;
+ }
+ else /* we cannot do much :( */
+ { ++(ccache[i].c_errors);
+ handle_error_cluster(&(ccache[i]));
+ }
+ }
+ unlock_ch(&(ccache[i]));
+ lock_ccache();
+ --(ccache[i].c_count);
+
+ /* we must not retry here since this causes an endless loop
+ in case of a write error...
+ unlock_ccache();
+ goto retry;
+ */
+ }
+ }
+
+ unlock_ccache();
+}
+
+/* cluster delete - deletes this cluster from the cache and cancels ch_dirty
+ -- called when cluster is deleted in FAT */
+void delete_cache_cluster(struct super_block*sb, int clusternr)
+{ Cluster_head*ch;
+
+ /* gets always a locked cluster and doesn't force read from disk */
+ ch=get_ch(sb,clusternr);
+
+ /* it is really necessary that get_ch never fails, but .... */
+ if(ch==NULL)
+ { printk(KERN_WARNING "DMSDOS: delete_cache_cluster: get_ch returned NULL\n");
+ /* we can't do anything here, so just assume Murphy is not there :) */
+ /* well of course, failed get_ch means the cluster is surely not in the
+ cache */
+ dmsdos_write_cluster(sb,NULL,0,clusternr,0,0);
+ return;
+ }
+
+ /* inform the cluster handling routines so they can delete pointers in fs */
+ dmsdos_write_cluster(sb,NULL,0,clusternr,0,0);
+
+ /* if someone is using the cluster except us he has bad luck */
+ if(ch->c_flags!=C_NOT_MALLOCD)ch->c_flags=C_DELETED;
+ ch->c_clusternr=-1; /* :) */
+
+ unlock_ch(ch);
+ ch_free(ch);
+}
+
+/* for daemon write */
+int daemon_write_cluster(struct super_block*sb,unsigned char*data,
+ int len, int clusternr, int rawlen)
+{ Cluster_head*ch;
+ int i;
+
+ /* gets always a locked cluster slot and doesn't force read from disk */
+ ch=get_ch(sb,clusternr);
+ if(ch==NULL)
+ { /* sorry */
+ printk(KERN_ERR "DMSDOS: daemon_write_cluster: ch==NULL\n");
+ return -EIO;
+ }
+
+ i=dmsdos_write_cluster(sb,data,len,clusternr,0,-rawlen);
+ unlock_ch(ch);
+ ch_free(ch);
+
+ return i;
+}
+
+void log_ccache_statistics()
+{ int j;
+ int free;
+ int valid;
+ int invalid;
+ int dirty_n;
+ int dirty_u;
+ int del;
+ int n_mallocd;
+
+ lock_ccache();
+
+ printk(KERN_INFO "DMSDOS: ccache statistics:\n");
+
+ free=0;
+ valid=0;
+ invalid=0;
+ dirty_n=0;
+ dirty_u=0;
+ del=0;
+ n_mallocd=0;
+
+ for(j=0;j<CCACHESIZE;++j)
+ { switch(ccache[j].c_flags)
+ { case C_FREE: ++free; break;
+ case C_VALID: ++valid; break;
+ case C_INVALID: ++invalid; break;
+ case C_DIRTY_NORMAL: ++dirty_n; break;
+ case C_DIRTY_UNCOMPR: ++dirty_u; break;
+ case C_DELETED: ++del; break;
+ case C_NOT_MALLOCD: ++n_mallocd; break;
+ default: printk(KERN_ERR "DMSDOS: log_ccache_statistics: cannot happen.\n");
+ }
+ }
+
+ printk(KERN_INFO "sum: free=%d valid=%d invalid=%d dirty_n=%d dirty_u=%d del=%d n_mallocd=%d\n",
+ free,valid,invalid,dirty_n,dirty_u,del,n_mallocd);
+
+ unlock_ccache();
+}
+
+void get_memory_usage_ccache(int*used, int*max)
+{ int i;
+ int size=0;
+ int usedcount=0;
+ Dblsb*dblsb;
+
+ for(i=0;i<CCACHESIZE;++i)
+ { switch(ccache[i].c_flags)
+ { case C_VALID:
+ case C_DIRTY_NORMAL:
+ case C_DIRTY_UNCOMPR:
+ dblsb=MSDOS_SB(ccache[i].c_sb)->private_data;
+ size+=dblsb->s_sectperclust*SECTOR_SIZE;
+ usedcount++;
+ }
+ }
+ if(used)*used=size;
+ if(max)
+ { /* unknown due to possibly different cluster sizes */
+ /* we estimate :) */
+ if(usedcount==0)*max=0; /* nothing in use, we can't estimate :( */
+ else *max=(CCACHESIZE*size)/usedcount;
+ }
+}
+
+/**********************************************************************/
+
+
+struct buffer_head* dblspace_bread(struct super_block*sb,int vsector)
+{ int dbl_clust;
+ int sect_offs;
+ struct buffer_head*bh;
+ Cluster_head*ch;
+ Dblsb*dblsb=MSDOS_SB(sb)->private_data;
+
+ if(vsector>=FAKED_ROOT_DIR_OFFSET&&
+ vsector<FAKED_ROOT_DIR_OFFSET+dblsb->s_rootdirentries/16)
+ { LOG_LLRW("DMSDOS: read_virtual_sector: rootdir sector_offset %d\n",
+ vsector-FAKED_ROOT_DIR_OFFSET);
+ return raw_bread(sb,dblsb->s_rootdir+vsector-FAKED_ROOT_DIR_OFFSET);
+ }
+
+ if(vsector<FAKED_DATA_START_OFFSET)
+ { err:
+ printk(KERN_ERR "DMSDOS: illegal virtual sector %d, can't map to real sector\n",
+ vsector);
+ *(int*)0=0;
+ return NULL;
+ }
+ dbl_clust=((vsector-FAKED_DATA_START_OFFSET)/dblsb->s_sectperclust)+2;
+ if(dbl_clust>dblsb->s_max_cluster)goto err;
+ sect_offs=(vsector-FAKED_DATA_START_OFFSET)%dblsb->s_sectperclust;
+
+ ch=ch_read(sb,dbl_clust,0); /*we need to read and shouldn't need a lock*/
+ if(ch==NULL)
+ { printk(KERN_ERR "DMSDOS: read_virtual_sector: read_cluster failed!\n");
+ return NULL;
+ }
+ /* now setup a fake buffer_head */
+ bh=MALLOC(sizeof(struct buffer_head));
+ if(bh==NULL)
+ { printk(KERN_ERR "DMSDOS: read_virtual_sector: no memory!\n");
+ /*FREE(clusterd);*/
+ ch_free(ch);
+ return NULL;
+ }
+ bh->b_state=FAKED_BH_MAGIC;
+
+ /* we must share the data */
+ bh->b_data=&(ch->c_data[sect_offs*SECTOR_SIZE]);
+ /* the cluster is *not* free yet, so DON'T call ch_free here */
+ /* instead copy a pointer to the cluster_head somewhere so we can free it
+ later */
+ bh->b_next=(struct buffer_head*)ch; /*the cast avoids compiler warning */
+ return bh;
+}
+
+struct buffer_head *dblspace_getblk (
+ struct super_block *sb,
+ int block)
+{
+ struct buffer_head*ret;
+ ret=dblspace_bread(sb,block);
+ if(ret==NULL)return ret;
+ return ret;
+}
+
+void dblspace_brelse(struct super_block* sb,struct buffer_head*bh)
+{ if(bh)
+ { if(bh->b_state==FAKED_BH_MAGIC)
+ { /* we free the cluster instead */
+ /* a pointer to ch was saved in b_next */
+ /* uh, we must check the size first.... uh no this is for bh_dirty.. */
+ Cluster_head*ch=(Cluster_head*)bh->b_next;
+ /*if((unsigned long)bh->b_data-(unsigned long)ch->c_data+SECTOR_SIZE>ch->c_length)
+ ch->c_length=(unsigned long)bh->b_data-(unsigned long)ch->c_data+SECTOR_SIZE;*/
+ ch_free(ch);
+ FREE(bh);
+ return;
+ }
+ }
+ raw_brelse(sb,bh);
+}
+
+
+void dblspace_mark_buffer_dirty(struct super_block*sb,struct buffer_head*bh,
+ int dirty_val)
+{ int i;
+
+ if(dirty_val==0)return;
+
+ if(sb->s_flags&MS_RDONLY)
+ { printk(KERN_ERR "DMSDOS: dblspace_mark_buffer_dirty: READ-ONLY filesystem\n");
+ return;
+ }
+
+ if(bh)
+ { if(bh->b_state==FAKED_BH_MAGIC)
+ {
+ /* a copy of ch was saved in b_next */
+ Cluster_head*ch=(Cluster_head*)bh->b_next;
+ struct super_block*sb=ch->c_sb;
+ Dblsb*dblsb=MSDOS_SB(sb)->private_data;
+
+ /* ensure cluster is marked large enough to hold the virtual sector */
+ if((unsigned long)bh->b_data-(unsigned long)ch->c_data+SECTOR_SIZE>
+ ch->c_length)
+ ch->c_length=(unsigned long)bh->b_data-(unsigned long)ch->c_data+
+ SECTOR_SIZE;
+
+ /* the virtual sector code is usually called by directory handling
+ routines, so we check whether we may compress a directory
+ -- file_write calles ch_dirty directly */
+ i=ch_dirty(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*/
+ {
+ /* now we have a serious problem here...
+ ch_dirty failed... we are in danger of losing the data....
+ */
+ printk(KERN_CRIT "DMSDOS: Dirty virtual sector cannot be written - FILESYSTEM DAMAGE POSSIBLE! Trying to delay write.\n");
+ ch_dirty_retry_until_success(ch,0,
+ DIR_MAY_BE_COMPRESSED(dblsb)?UC_NORMAL:UC_UNCOMPR);
+ }
+ return;
+ }
+ }
+
+ raw_mark_buffer_dirty(sb,bh,1);
+}
+
+void dblspace_set_uptodate (
+ struct super_block *sb,
+ struct buffer_head *bh,
+ int val)
+{
+ if(bh->b_state==FAKED_BH_MAGIC)return;
+ raw_set_uptodate(sb,bh,val);
+}
+
+int dblspace_is_uptodate (
+ struct super_block *sb,
+ struct buffer_head *bh)
+{
+ if(bh->b_state==FAKED_BH_MAGIC)return 1;
+ return raw_is_uptodate(sb,bh);
+}
+
+void dblspace_ll_rw_block (
+ struct super_block *sb,
+ int opr,
+ int nbreq,
+ struct buffer_head *bh[32])
+{
+ /* we just cannot do low-level access here */
+ /* this might be wrong... */
+ /* as far as I know, only the read_ahead code in fat/file.c uses
+ this... well we do our own read-ahead in dmsdos, so what... */
+ return;
+}
+
+#ifdef USE_READA_LIST
+
+DECLARE_MUTEX(reada_sem); /* Must be initialized to green light */
+void lock_reada(void) {down(&reada_sem);}
+void unlock_reada(void) {up(&reada_sem);}
+struct
+{ struct buffer_head*bh;
+ struct super_block*sb;
+} reada_list[READA_LIST_SIZE];
+int ra_pos=0;
+
+void init_reada_list(void)
+{ int i;
+
+ for(i=0;i<READA_LIST_SIZE;++i)
+ { reada_list[i].bh=NULL;
+ reada_list[i].sb=NULL;
+ }
+}
+
+void kill_reada_list_dev(int dev)
+{ int i;
+
+ lock_reada();
+
+ for(i=0;i<READA_LIST_SIZE;++i)
+ { if(reada_list[i].bh)
+ { if(reada_list[i].sb->s_dev==dev)
+ { raw_brelse(reada_list[i].sb,reada_list[i].bh);
+ reada_list[i].bh=NULL;
+ reada_list[i].sb=NULL;
+ }
+ }
+ }
+
+ unlock_reada();
+}
+
+void stack_reada(struct super_block*sb,struct buffer_head*bh)
+{
+ lock_reada();
+
+ if(reada_list[ra_pos].bh)
+ raw_brelse(reada_list[ra_pos].sb,reada_list[ra_pos].bh);
+
+ reada_list[ra_pos].bh=bh;
+ reada_list[ra_pos].sb=sb;
+
+ ++ra_pos;
+ if(ra_pos>=READA_LIST_SIZE)ra_pos=0;
+
+ unlock_reada();
+}
+#endif /*USE_READA_LIST*/
+
+void dblspace_reada(struct super_block*sb, int sector,int count)
+{
+ struct buffer_head*bhlist[MAX_READA];
+ int i;
+ struct buffer_head**bhlist2=bhlist;
+
+ if(count>MAX_READA)count=MAX_READA;
+
+ i=0;
+ while(i<count)
+ {
+ bhlist[i]=raw_getblk(sb,sector+i); /* get without read */
+ if(bhlist[i]==NULL)break; /* failed, give up */
+ if(raw_is_uptodate(sb,bhlist[i]))
+ { raw_brelse(sb,bhlist[i]); /* according to breada the buffer must be
+ freed here */
+ break; /* has already been read, abort */
+ }
+ ++i;
+ }
+
+ count=i;
+
+ if(count==0)return;
+
+ /* uhh.. there's a hard limit of 32 in the fat filesystem... */
+ if(count/32)
+ { for(i=0;i<count/32;++i)
+ { raw_ll_rw_block(sb,READA,32,bhlist2);
+ bhlist2+=32;
+ }
+ }
+ if(count%32)raw_ll_rw_block(sb,READA,count%32,bhlist2);
+ /* place read command in list, but don't wait for it to finish */
+
+ for(i=0;i<count;++i)
+#ifdef USE_READA_LIST
+ stack_reada(sb,bhlist[i]);
+#else
+ /* not a good idea - brelse calls wait_on_buffer....... */
+ raw_brelse(sb,bhlist[i]);
+#endif
+}
diff --git a/src/dcread.c b/src/dcread.c
new file mode 100644
index 0000000..48b62e0
--- /dev/null
+++ b/src/dcread.c
@@ -0,0 +1,344 @@
+/*
+
+dcread.c
+
+DMSDOS: example program illustrating how to use the dmsdos library.
+
+******************************************************************************
+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.
+******************************************************************************
+
+
+This is an example how to use the dmsdos library. This program displays
+a cluster on the screen in one of several formats (hexdump, text, etc.).
+It can also search a file through the directories.
+
+For documentation about the dmsdos library see file libdmsdos.doc.
+
+Warning: This utility is not perfect. It does not check file end properly.
+It does not even distinguish between files and directories. It does not
+support long file names. And the file name conversion to 8.3 name space is
+far away from good. But example code never has to be perfect :)
+
+There's also no documentation how to use this program except the usage
+line. Example code never has documentation. Well, yes, you are expected
+to read through the source code. :)
+
+*/
+
+#include<stdio.h>
+#include<stdlib.h>
+#include<string.h>
+#include<ctype.h>
+
+#include"dmsdos.h"
+#include"lib_interface.h"
+
+#define M_RAW 1
+#define M_HEX 0
+#define M_DIR 2
+#define M_TXT 3
+#define M_DISPLAYMASK 3
+#define M_VERBOSE 16
+
+/*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 display_cluster(int nr, int mode)
+{ 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(mode&M_VERBOSE)fprintf(stderr,"cluster %d has length %d\n",nr,i);
+
+ switch(mode&M_DISPLAYMASK)
+ { case M_RAW:
+ for(j=0;j<dblsb->s_sectperclust*512;++j)printf("%c",data[j]);
+ break;
+ case M_HEX:
+ for(j=0;j<dblsb->s_sectperclust*512;j+=16)
+ { char buf[100];
+ char str[100];
+
+ sprintf(str,"%04X:",j);
+ for(i=0;i<16;++i)
+ { sprintf(buf," %02X",data[j+i]);
+ strcat(str,buf);
+ }
+ strcat(str," ");
+ for(i=0;i<16;++i)
+ { if(data[i+j]>=32&&data[i+j]<=126)sprintf(buf,"%c",data[i+j]);
+ else strcpy(buf,".");
+ strcat(str,buf);
+ }
+
+ printf("%s\n",str);
+ }
+ break;
+ case M_DIR:
+ for(j=0;j<dblsb->s_sectperclust*512;j+=32)
+ { unsigned char*pp;
+ unsigned int x;
+
+ if(data[j]==0)break;
+ if(data[j]==0xe5){printf("--DELETED--\n");continue;}
+ for(i=0;i<11;++i)
+ { if(i==8)printf(" ");
+ printf("%c",data[j+i]);
+ }
+ 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+22]);
+ x=CHS(pp);
+ printf(" %02d:%02d:%02d",x>>11,(x>>5)&63,(x&31)<<1);
+
+ pp=&(data[j+24]);
+ x=CHS(pp);
+ printf(" %02d.%02d.%04d",x&31,(x>>5)&15,(x>>9)+1980); /* y2k compliant :) */
+
+ pp=&(data[j+26]);
+ printf(" %5d",CHS(pp));
+
+ pp=&(data[j+28]);
+ printf(" %7lu\n",CHL(pp));
+ }
+ break;
+ case M_TXT:
+ i=0;
+ for(j=0;j<dblsb->s_sectperclust*512;j++)
+ { if(data[j]==10)
+ { printf("\n");
+ i=0;
+ continue;
+ }
+ if(data[j]>=32&&data[j]<=126)printf("%c",data[j]);
+ else printf(".");
+ ++i;
+ if(i==80&&j<511&&data[j+1]!=10)
+ { printf("\n");
+ i=0;
+ }
+ }
+ if(i)printf("\n");
+ break;
+ default:
+ fprintf(stderr,"display mode not implemented\n");
+ free(data);
+ return -1;
+ }
+
+ free(data);
+ return 0;
+}
+
+int display_chain(int start, int mode)
+{ int i,next;
+
+ if(start==0)return display_cluster(0,mode);
+ if(start==1||start<0||start>dblsb->s_max_cluster)return -1;
+
+ do
+ {
+ next=dbl_fat_nextcluster(sb,start,NULL);
+ if(next==0&&(mode&M_VERBOSE)!=0)
+ fprintf(stderr,"warning: cluster %d is marked as unused in FAT\n",start);
+ i=display_cluster(start,mode);
+ if(i<0)return i;
+ start=next;
+ }
+ while(next>1&&next<=dblsb->s_max_cluster);
+
+ if(next>=0)
+ { fprintf(stderr,"chain has no valid end in FAT\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+int scan_dir(char*entry,int start)
+{ 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);
+ 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 i;
+ char*p;
+
+ for(p=strtok(path,"/");p;p=strtok(NULL,"/"))
+ { i=scan_dir(p,start);
+ 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;
+
+ if(argc<3)
+ { fprintf(stderr,"usage: dcread CVF cluster|/path/to/file [raw|dir|hex|txt]\n");
+ return 1;
+ }
+
+ sb=open_cvf(argv[1],0/*read-only*/);
+ if(sb==NULL)
+ { printf("open CVF %s failed\n",argv[1]);
+ return 2;
+ }
+ dblsb=MSDOS_SB(sb)->private_data;
+
+ if(*(argv[2])=='/')
+ { cluster=scan_path(argv[2]+1,0);
+ if(cluster<0)
+ { fprintf(stderr,"%s not found\n",argv[2]);
+ return 1;
+ }
+ }
+ else cluster=scan(argv[2]);
+
+ if(argc==4)
+ { if(strcmp(argv[3],"raw")==0)mode=M_RAW;
+ else if(strcmp(argv[3],"dir")==0)mode=M_DIR;
+ else if(strcmp(argv[3],"hex")==0)mode=M_HEX;
+ else if(strcmp(argv[3],"txt")==0)mode=M_TXT;
+ else
+ { fprintf(stderr,"invalid argument %s\n",argv[3]);
+ close_cvf(sb);
+ return 1;
+ }
+ }
+
+ i=display_chain(cluster,mode|M_VERBOSE);
+
+ close_cvf(sb);
+
+ return i;
+}
diff --git a/src/dmsdos-config.default b/src/dmsdos-config.default
new file mode 100644
index 0000000..c402af5
--- /dev/null
+++ b/src/dmsdos-config.default
@@ -0,0 +1,65 @@
+#
+# Defaults for a safe dmsdos configuration. Do not edit this file.
+#
+
+#
+# CVF formats to be supported
+#
+DMSDOS_CONFIG_DBLSP_DRVSP=y
+DMSDOS_CONFIG_DRVSP3=y
+DMSDOS_CONFIG_STAC3=y
+DMSDOS_CONFIG_STAC4=y
+
+#
+# Memory Management
+#
+# USE_XMALLOC is not set
+USE_VMALLOC=y
+
+#
+# Cache setup
+#
+LISTSIZE=1024
+MDFATCACHESIZE=40
+DFATCACHESIZE=20
+BITFATCACHESIZE=10
+MAX_CACHE_TIME=60
+CCACHESIZE=64
+MAX_CCACHE_TIME=240
+
+#
+# Read-ahead Options
+#
+MAX_READA=64
+USE_READA_LIST=y
+READA_LIST_SIZE=256
+READA_THRESHOLD=4095
+
+#
+# Misc Options
+#
+DBL_WRITEACCESS=y
+DEFAULT_LOGLEVEL=0
+# NOLOG is not set
+DEFAULT_CF=11
+# SEQLOG is not set
+DMSDOS_USE_READPAGE=y
+
+#
+# Internal compression daemon
+#
+# INTERNAL_DAEMON is not set
+IDMSDOSD_TIME=30
+
+#
+# Speedup Tricks & Hacks
+#
+SP_BIT0=y
+SP_BIT1=y
+# SP_BIT2 is not set
+# SP_BIT3 is not set
+SP_BIT4=y
+SP_BIT5=y
+# SP_BIT6 is not set
+SP_BIT7=y
+# SP_BIT8 is not set
diff --git a/src/dmsdos-config.h.default b/src/dmsdos-config.h.default
new file mode 100644
index 0000000..08ff27e
--- /dev/null
+++ b/src/dmsdos-config.h.default
@@ -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/dmsdos.h b/src/dmsdos.h
new file mode 100644
index 0000000..5606f52
--- /dev/null
+++ b/src/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/dmsdosfsck.c b/src/dmsdosfsck.c
new file mode 100644
index 0000000..8e66b7f
--- /dev/null
+++ b/src/dmsdosfsck.c
@@ -0,0 +1,674 @@
+/*
+
+dmsdosfsck.c
+
+DMSDOS: filesystem check utility for CVFs.
+
+******************************************************************************
+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<unistd.h>
+#include"dmsdos.h"
+#include"lib_interface.h"
+
+int check_dir(int parent,int clusternr);
+
+/*this is not good - but currently we have only one CVF open at a time*/
+struct super_block*sb;
+Dblsb*dblsb;
+
+typedef struct
+{ int referrenced_from;
+ int start;
+} Fatdata;
+
+Fatdata fat[65536];
+
+#define FAT_EOF -1
+
+int seenlist[65536];
+int repair_automatically=0;
+int repair_interactively=0;
+int verbose=0;
+int listfiles=0;
+
+#define vprintf if(verbose)printf
+#define lprintf if(listfiles)printf
+
+int repair(char*text)
+{ int c;
+
+ if(repair_automatically)return 1;
+ if(repair_interactively==0)return 0;
+ fflush(stdin);
+ printf("%s",text);
+ fflush(stdout);
+ c=fgetc(stdin);
+ fflush(stdin);
+ if(c=='y'||c=='Y')return 1;
+ return 0;
+}
+
+int check_fat_loop(int begin)
+{ int seen=0;
+ int next;
+ int i;
+ int newval;
+
+ while(begin>=2&&begin<=dblsb->s_max_cluster)
+ { /* add to seenlist */
+ seenlist[seen]=begin;
+ ++seen;
+ if(seen>65535)return -1; /* cannot happen */
+ next=dbl_fat_nextcluster(sb,begin,NULL);
+
+ /* check whether it was already seen */
+ for(i=0;i<seen;++i)
+ { if(seenlist[i]==next)
+ { /* here begins a fat loop */
+ printf("FAT loop at cluster %d found.\n",begin);
+ if(repair("Break it?")==0)return 1;
+ newval=FAT_EOF;
+ dbl_fat_nextcluster(sb,begin,&newval);
+ return 0;
+ }
+ }
+
+ begin=next;
+ }
+
+ return 0;
+}
+
+int check_fat()
+{ int i;
+ int val;
+ int errors=0;
+ int newval=0;
+
+ for(i=0;i<65536;++i)
+ { fat[i].referrenced_from=0;
+ fat[i].start=0;
+ }
+
+ for(i=2;i<=dblsb->s_max_cluster;++i)
+ { vprintf("Checking cluster %d...\n",i);
+
+ val=dbl_fat_nextcluster(sb,i,NULL);
+ if(val)
+ { if(val==1)
+ { printf("cluster %d: invalid fat entry\n",i);
+ if(repair("Correct it?")==0)++errors;
+ else
+ { newval=FAT_EOF;
+ dbl_fat_nextcluster(sb,i,&newval);
+ }
+ }
+ if(check_fat_loop(i))
+ { printf("Unresolved FAT loop in filesystem. Can't continue, sorry.\n");
+ close_cvf(sb);
+ exit(4);
+ }
+ }
+ if(val>=2&&val<=dblsb->s_max_cluster)
+ { if(fat[val].referrenced_from)
+ { /* this is a crosslink */
+ printf("cluster %d is crosslinked with %d to %d\n",
+ i,fat[val].referrenced_from,val);
+ if(repair("Break it?")==0)++errors;
+ else
+ { newval=FAT_EOF;
+ dbl_fat_nextcluster(sb,i,&newval);
+ dbl_fat_nextcluster(sb,fat[val].referrenced_from,&newval);
+ fat[val].referrenced_from=0;
+ }
+ }
+ fat[val].referrenced_from=i;
+ }
+ }
+
+ return errors;
+}
+
+int check_chains()
+{ int i;
+ int val;
+ int errors=0;
+ int newval=0;
+ int next;
+
+ for(i=2;i<=dblsb->s_max_cluster;++i)
+ { val=dbl_fat_nextcluster(sb,i,NULL);
+ if(val>=2&&val<=dblsb->s_max_cluster&&fat[i].referrenced_from==0)
+ { /* this is the start of a chain */
+ vprintf("checking chain beginning at cluster %d...\n",i);
+
+ rchain:
+ next=dbl_fat_nextcluster(sb,val,NULL);
+ if(next==FAT_EOF)continue;
+
+ if(next==0||next>dblsb->s_max_cluster)
+ { printf("chain breaks unexpectedly.\n");
+ if(repair("Set proper end?")==0)++errors;
+ else
+ { newval=FAT_EOF;
+ dbl_fat_nextcluster(sb,val,&newval);
+ }
+ }
+ else
+ { val=next;
+ goto rchain;
+ }
+
+ }
+ }
+ return 0;
+}
+
+struct nametest
+{ unsigned char name[12];
+ struct nametest*next;
+} nametest;
+
+int add_name(struct nametest**namelist,unsigned char*text)
+{ struct nametest*akt=*namelist;
+
+ while(akt)
+ { if(strncmp(akt->name,text,11)==0)return -1;
+ akt=akt->next;
+ }
+
+ akt=malloc(sizeof(struct nametest));
+ if(akt==NULL)return -2;
+
+ strncpy(akt->name,text,11);
+ akt->next=*namelist;
+ *namelist=akt;
+
+ return 0;
+}
+
+void free_namelist(struct nametest**namelist)
+{ struct nametest*akt=*namelist;
+ struct nametest*merk;
+
+ while(akt)
+ { merk=akt->next;
+ free(akt);
+ akt=merk;
+ }
+
+ *namelist=NULL;
+}
+
+int check_char(unsigned char c,int noprint)
+{ if(c==0x5)c=0xE5; /* M$ hack for languages where E5 is a valid char */
+ if(c<32||strchr("+\\?*<>|\"=,;",c)!=NULL)
+ { if(!noprint)lprintf("?");
+ return 1;
+ }
+ if(!noprint)lprintf("%c",c);
+ return 0;
+}
+
+int check_direntry(int dirstartclust, unsigned char*data, int*need_write,
+ struct nametest**namelist)
+{ int i;
+ unsigned int x;
+ unsigned char*pp;
+ unsigned long size;
+ int cluster, prevcluster, newval;
+ unsigned long fatsize;
+ int clustersize;
+ int invchar;
+
+ *need_write=0;
+
+ if(data[0]==0||data[0]==0xE5)return 0;
+
+ if(data[11]&8)return 0; /* ignore V entries */
+
+ invchar=0;
+ for(i=0;i<11;++i)
+ { if(i==8)lprintf(" ");
+ invchar+=check_char(data[i],0);
+ }
+
+ if(listfiles)
+ {
+ printf(" ");
+ if(data[11]&1)printf("R");else printf(" ");
+ if(data[11]&2)printf("H");else printf(" ");
+ if(data[11]&4)printf("S");else printf(" ");
+ if(data[11]&8)printf("V");else printf(" ");
+ if(data[11]&16)printf("D");else printf(" ");
+ if(data[11]&32)printf("A");else printf(" ");
+ if(data[11]&64)printf("?");else printf(" ");
+ if(data[11]&128)printf("?");else printf(" ");
+
+ pp=&(data[22]);
+ x=CHS(pp);
+ printf(" %02d:%02d:%02d",x>>11,(x>>5)&63,(x&31)<<1);
+
+ pp=&(data[24]);
+ x=CHS(pp);
+ printf(" %02d.%02d.%4d",x&31,(x>>5)&15,(x>>9)+1980); /* y2k compliant :) */
+ }
+
+ pp=&(data[26]);
+ cluster=CHS(pp);
+ lprintf(" %5d",cluster);
+
+ pp=&(data[28]);
+ size=CHL(pp);
+ lprintf(" %7lu ",size);
+
+ if(invchar)
+ { printf("name has invalid chars, ");
+ if(repair("Replace them?")!=0)
+ { for(i=0;i<11;++i)
+ { if(check_char(data[i],1))
+ { data[i]='~';
+ *need_write=1;
+ }
+ }
+ }
+ }
+
+ if(add_name(namelist,data))
+ { printf("duplicate filename ");
+ for(i=0;i<11;++i)
+ { if(i==8)printf(" ");
+ printf("%c",data[i]);
+ }
+ printf("\n");
+
+ if(repair("Rename?")!=0)
+ { char teststr[10];
+ int n=1;
+
+ i=7;
+ while(i>=0)
+ { if(data[i]==' ')data[i]='~';
+ else break;
+ --i;
+ }
+
+ do
+ { sprintf(teststr,"~%d",n++);
+ for(i=0;i<strlen(teststr);++i)data[i+8-strlen(teststr)]=teststr[i];
+ }
+ while(add_name(namelist,data));
+ *need_write=1;
+ }
+ }
+
+ if(cluster<0||cluster==1||cluster>dblsb->s_max_cluster)
+ { printf("clusternr invalid\n");
+ if(repair("Truncate?")==0)return -1;
+ data[26]=0;
+ data[27]=0;
+ cluster=0;
+ *need_write=1;
+ }
+ else
+ { if(cluster)
+ { if(fat[cluster].referrenced_from!=0||fat[cluster].start!=0)
+ { printf("first cluster crosslink\n");
+ if(repair("Truncate?")==0)return -1;
+ data[26]=0;
+ data[27]=0;
+ cluster=0;
+ *need_write=1;
+ }
+ else fat[cluster].start=1;
+ }
+ }
+
+ if(data[11]&16) /* dir */
+ { if(cluster==0)
+ { printf("clusternr invalid for subdir\n");
+ goto irrepdir;
+ }
+ lprintf("OK\n");
+ lprintf("descending directory...\n");
+ i=check_dir(dirstartclust,cluster);
+ lprintf("ascending...\n");
+ if(i>=0)
+ { if(size)
+ { printf("directory entry has size !=0\n");
+ if(repair("Correct this?")==0)++i;
+ else
+ { data[28]=0;
+ data[29]=0;
+ data[30]=0;
+ data[31]=0;
+ size=0;
+ *need_write=1;
+ }
+ }
+ return i;
+ }
+ irrepdir:
+ printf("directory is irreparably damaged");
+ if(repair("Convert to file?")==0)return -1;
+ data[11]&=~16;
+ *need_write=1;
+ /* fall through */
+ }
+
+ if(cluster==0)
+ { if(size==0)
+ { lprintf("OK\n");
+ return 0;
+ }
+ printf("wrong size\n");
+ if(repair("Correct this?")==0)return -1;
+ else
+ { data[28]=0;
+ data[29]=0;
+ data[30]=0;
+ data[31]=0;
+ size=0;
+ *need_write=1;
+ }
+ return 0;
+ }
+
+ clustersize=dblsb->s_sectperclust*SECTOR_SIZE;
+
+ fatsize=0;
+ prevcluster=0;
+ while(cluster>1&&cluster<=dblsb->s_max_cluster)
+ { prevcluster=cluster;
+ cluster=dbl_fat_nextcluster(sb,cluster,NULL);
+ fatsize+=clustersize;
+ }
+ if(cluster==0)
+ { printf("fat alloc ends with zero\n");
+ cluster=prevcluster;
+ fatsize-=clustersize;
+ if(repair("Correct this?")==0)return -1;
+ newval=FAT_EOF;
+ dbl_fat_nextcluster(sb,cluster,&newval);
+ }
+ if(cluster==1||cluster>dblsb->s_max_cluster)
+ { printf("fat alloc invalid\n");
+ return -1;
+ }
+
+ if(size==fatsize)
+ { lprintf("OK\n");
+ return 0;
+ }
+ if(size/clustersize==(fatsize-1)/clustersize)
+ { lprintf("OK\n");
+ return 0;
+ }
+ printf("file size wrong\n");
+ if(repair("Recalculate file size?")==0)return -1;
+ data[28]=fatsize;
+ data[29]=fatsize>>8;
+ data[30]=fatsize>>16;
+ data[31]=fatsize>>24;
+ *need_write=1;
+ return 0;
+}
+
+int check_root_dir(void)
+{ int i,j,errors,r;
+ struct buffer_head*bh;
+ int need_write=0;
+ struct nametest*namelist=NULL;
+
+ errors=0;
+
+ for(i=0;i<dblsb->s_rootdirentries/16;++i)
+ { bh=raw_bread(sb,dblsb->s_rootdir+i);
+ if(bh==NULL)return -1;
+ for(j=0;j<16;++j)
+ { r=check_direntry(0,&(bh->b_data[j*32]),&need_write,&namelist);
+ if(r)++errors;
+ else if(need_write)raw_mark_buffer_dirty(sb,bh,1);
+ }
+ raw_brelse(sb,bh);
+ }
+
+ free_namelist(&namelist);
+ return errors;
+}
+
+int check_dir(int parent,int clusternr)
+{ unsigned char*data;
+ int j,errors,r;
+ int next;
+ int start=2;
+ int dirstartclust=clusternr;
+ int need_write=0;
+ int len;
+ struct nametest*namelist=NULL;
+
+ errors=0;
+ data=malloc(dblsb->s_sectperclust*SECTOR_SIZE);
+ if(data==NULL)return -1;
+
+ vprintf("checking directory at start cluster %d...\n",clusternr);
+
+ next=dbl_fat_nextcluster(sb,clusternr,NULL);
+ if(next==0)
+ { printf("warning: dir cluster %d is marked free\n",clusternr);
+ }
+
+ while(clusternr>0)
+ { len=dmsdos_read_cluster(sb,data,clusternr);
+ if(len<0){free(data);return -1;}
+
+ if(start)
+ { unsigned char*pp;
+
+ /* check . 12345678EXT */
+ if(strncmp(data,". ",11)!=0)
+ { printf("first entry is not '.'\n");
+ ++errors;
+ }
+ pp=&(data[26]);
+ if(CHS(pp)!=clusternr)
+ { printf("self cluster nr is wrong in '.'\n");
+ ++errors;
+ }
+
+ /* check .. 12345678EXT */
+ if(strncmp(data+32,".. ",11)!=0)
+ { printf("second entry is not '..'\n");
+ ++errors;
+ }
+ pp=&(data[26+32]);
+ if(CHS(pp)!=parent)
+ { printf("parent cluster nr is wrong in '..'\n");
+ ++errors;
+ }
+
+ if(errors)
+ { printf("This doesn't look like a directory, skipped\n");
+ free(data);
+ return -1;
+ }
+ }
+
+ for(j=start;j<dblsb->s_sectperclust*16;++j)
+ { r=check_direntry(dirstartclust,&(data[j*32]),&need_write,&namelist);
+ if(r)++errors;
+ else if(need_write)dmsdos_write_cluster(sb,data,len,clusternr,0,1);
+ }
+
+ start=0;
+ next=dbl_fat_nextcluster(sb,clusternr,NULL);
+ if(next==1||next>dblsb->s_max_cluster)
+ { printf("directory ends with fat alloc error\n");
+ ++errors;
+ break;
+ }
+ clusternr=next;
+ }
+
+ free(data);
+ free_namelist(&namelist);
+ return errors;
+}
+
+int check_unused_chains()
+{ int i;
+ int val;
+ int errors=0;
+
+ for(i=2;i<=dblsb->s_max_cluster;++i)
+ { val=dbl_fat_nextcluster(sb,i,NULL);
+ if(val>=2&&val<=dblsb->s_max_cluster&&fat[i].referrenced_from==0
+ &&fat[i].start==0)
+ { vprintf("chain beginning with cluster %d is unused.\n",i);
+ /* if(repair("Delete it?")==0)++errors;
+ else
+ { free_chain(sb,i);
+ }
+ */
+ ++errors;
+ }
+ }
+
+ return errors;
+}
+
+int main(int argc, char*argv[])
+{ int errors=0;
+ int i;
+ char*filename=NULL;
+
+ fprintf(stderr, "dmsdosfsck 0.0.2 ALPHA TEST (be extremely careful with repairs)\n");
+
+ if(argc==1)
+ { usage:
+ fprintf(stderr, "usage: dmsdosfsck [ -aflrtvVw ] [ -d path -d ... ] [ -u path -u ... ] device\n"
+ "-a automatically repair the file system\n"
+ "-d path (*) drop that file\n"
+ "-f (*) salvage unused chains to files\n"
+ "-l list path names\n"
+ "-r interactively repair the file system\n"
+ "-t (*) test for bad clusters\n"
+ "-u path (*) try to undelete that (non-directory) file\n"
+ "-v verbose mode\n"
+ "-V (*) perform a verification pass\n"
+ "-w (*) write changes to disk immediately\n"
+ "(*) not yet implemented but option accepted for dosfsck compatibility\n");
+ exit(16);
+ }
+
+ for(i=1;i<argc;++i)
+ { if(strcmp(argv[i],"-a")==0)repair_automatically=1;
+ else if(strcmp(argv[i],"-r")==0)repair_interactively=1;
+ else if(strcmp(argv[i],"-l")==0)listfiles=1;
+ else if(strcmp(argv[i],"-v")==0)verbose=1;
+ else if(argv[i][0]!='-')filename=argv[i];
+ }
+
+ if(filename==NULL)goto usage;
+
+ if(repair_automatically!=0||repair_interactively!=0)
+ { printf("\n\nWARNING: repair functions are incomplete. Interrupt within 5 seconds to abort.\7\n\n\n");
+ sleep(5);
+ }
+
+ sb=open_cvf(filename,repair_automatically|repair_interactively);
+ if(sb==NULL)
+ { fprintf(stderr,"open_cvf %s failed - maybe this isn't a CVF.\n",filename);
+ exit(16);
+ }
+
+ dblsb=MSDOS_SB(sb)->private_data;
+
+ printf("pass 1: checking FAT...\n");
+
+ i=check_fat();
+ if(i)
+ { printf("Filesystem has fatal FAT errors\n");
+ printf("Cannot continue, sorry.\n");
+ printf("Don't mount this filesystem, it may crash or hang the FAT driver.\n");
+ close_cvf(sb);
+ exit(4);
+ }
+
+ printf("pass 2: checking cluster chains...\n");
+
+ i=check_chains();
+ if(i)
+ { printf("filesystem has errors in cluster chains\n");
+ ++errors;
+ }
+
+ printf("pass 3: calling dmsdos simple_check...\n");
+
+ i=simple_check(sb,0);
+ if(i==-1) /* there were fatal FAT errors detected */
+ { printf("Filesystem still has fatal FAT errors\n");
+ printf("CANNOT HAPPEN. THIS IS A BUG.\n");
+ close_cvf(sb);
+ abort();
+ }
+ if(i)
+ { printf("filesystem has low-level structure errors\n");
+ if(repair("Try to fix them?")==0)++errors;
+ else
+ { i=simple_check(sb,1);
+ if(i)
+ { printf("couldn't fix all low-level structure errors\n");
+ ++errors;
+ }
+ }
+ }
+
+ printf("pass 4: checking directories...\n");
+
+ if(check_root_dir())
+ { printf("filesystem has msdos-level structure errors\n");
+ ++errors;
+ }
+
+ printf("pass 5: checking for unused chains...\n");
+
+ i=check_unused_chains();
+ if(i)
+ { printf("filesystem has unused cluster chains\n");
+ ++errors;
+ }
+
+ close_cvf(sb);
+
+ if(errors==0)
+ { printf("filesystem has no errors\n");
+ }
+
+ return (errors==0)?0:4;
+}
diff --git a/src/dstacker_alloc.c b/src/dstacker_alloc.c
new file mode 100644
index 0000000..a0b3762
--- /dev/null
+++ b/src/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/dstacker_compr.c b/src/dstacker_compr.c
new file mode 100644
index 0000000..ae7d8b6
--- /dev/null
+++ b/src/dstacker_compr.c
@@ -0,0 +1,1234 @@
+/*
+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. */
+#define C_ST_u16(p,v) {put_unaligned(v,((__u16*)p)++);}
+#define C_LD_u16(p,v) {v=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/dstacker_dec.c b/src/dstacker_dec.c
new file mode 100644
index 0000000..28ded1c
--- /dev/null
+++ b/src/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/dutil.c b/src/dutil.c
new file mode 100644
index 0000000..aa30db4
--- /dev/null
+++ b/src/dutil.c
@@ -0,0 +1,419 @@
+/*
+dutil.c
+
+DMSDOS CVF-FAT module: external dmsdos utility.
+
+******************************************************************************
+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 "dmsdos.h"
+#include<sys/ioctl.h>
+#include<sys/types.h>
+#include<sys/stat.h>
+#include<fcntl.h>
+#include<string.h>
+#include<errno.h>
+#include<unistd.h>
+#include<malloc.h>
+
+int scan(char*arg)
+{ int w;
+
+ if(strncmp(arg,"0x",2)==0)sscanf(arg+2,"%x",&w);
+ else sscanf(arg,"%d",&w);
+
+ return w;
+}
+
+void error(void)
+{ perror("ioctl failed");
+ exit();
+}
+
+int main(int argc, char*argv[])
+{ Dblsb dblsb;
+ int fd;
+ int ret;
+ unsigned long w[10];
+ Dblstat dblstat;
+ double ratio,dosratio;
+ struct
+ { unsigned long w;
+ unsigned char data[512];
+ } buffer;
+ int i;
+ char a[100];
+ char b[100];
+ char c[100];
+ Mdfat_entry mde;
+
+ if(argc<2)
+ {
+ printf("DMSDOS utility (C) 1995-1998 Frank Gockel, Pavel Pisa\n");
+ printf("compiled " __DATE__ " " __TIME__ " under dmsdos version %d.%d.%d%s\n\n",
+ DMSDOS_MAJOR,DMSDOS_MINOR,DMSDOS_ACT_REL,DMSDOS_VLT);
+
+ printf("Usage: %s (directory)\n",argv[0]);
+ printf(" %s (directory) cluster (clusterno)\n",argv[0]);
+ printf(" %s (directory) sector (sectorno) [(file)]\n",argv[0]);
+ printf(" %s (directory) rcluster (clusterno) [(file)]\n",argv[0]);
+ printf(" %s (directory) rrawcluster (clusterno) [(file)]\n",argv[0]);
+ printf(" %s (directory) bitfat (sectorno)\n",argv[0]);
+ printf(" %s (directory) setcomp (comp_option)\n",argv[0]);
+ printf(" %s (directory) setcf (cf_option)\n",argv[0]);
+ printf(" %s (directory) dumpcache\n",argv[0]);
+ printf(" %s (directory) synccache [(allow_daemon)]\n",argv[0]);
+ printf(" %s (directory) logstat\n",argv[0]);
+ printf(" %s (directory) memory\n",argv[0]);
+ printf(" %s (directory) checkfs [(repair)]\n",argv[0]);
+ printf(" %s (directory) setloglevel (value)\n",argv[0]);
+ printf(" %s (directory) setspeedup (value)\n",argv[0]);
+ return 0;
+ }
+
+ fd=open(argv[1],O_RDONLY);
+ if(fd<0)
+ { perror(argv[1]);
+ return 2;
+ }
+
+ /* this hack enables reverse version check */
+ /* it must not be changed in order to recognize incompatible older versions */
+ /* this also depends on s_dcluster being the first record in Dblsb */
+ dblsb.s_dcluster=DMSDOS_VERSION;
+
+ ret=ioctl(fd,DMSDOS_GET_DBLSB,&dblsb);
+ if(ret<0)
+ { printf("This is not a DMSDOS directory.\n");
+ close(fd);
+ return 2;
+ }
+ printf("You are running DMSDOS driver version %d.%d.%d.\n",(ret&0xff0000)>>16,
+ (ret&0x00ff00)>>8,ret&0xff);
+ /*printf("debug: ret=0x%08x\n",ret);*/
+ if(ret!=DMSDOS_VERSION)printf("This utility was compiled for DMSDOS version %d.%d.%d",
+ (DMSDOS_VERSION&0xff0000)>>16,(DMSDOS_VERSION&0x00ff00)>>8,DMSDOS_VERSION&0xff);
+ if(ret&0x0f000000)
+ { printf("\nSorry, this utility is too old for the actual DMSDOS driver version.\n");
+ close(fd);
+ return 2;
+ }
+ if(ret<0x00000902)
+ { printf("\nSorry, this utility requires at least DMSDOS driver version 0.9.2.\n");
+ close(fd);
+ return 2;
+ }
+ if(ret!=DMSDOS_VERSION)printf(" but should still work.\n\n");
+ else printf("\n");
+
+ printf("Parameters of the CVF the directory specified belongs to:\n");
+ printf("dcluster: %5d ",dblsb.s_dcluster);
+ printf("mdfatstart: %5d ",dblsb.s_mdfatstart);
+ printf("fatstart: %5d ",dblsb.s_fatstart);
+ printf("rootdir: %5d\n",dblsb.s_rootdir);
+ printf("root_entries:%5d ",dblsb.s_rootdirentries);
+ printf("sectperclust:%5d ",dblsb.s_sectperclust);
+ printf("bootblock: %5d ",dblsb.s_bootblock);
+ printf("16bitfat: %5s\n",dblsb.s_16bitfat?"yes":"no");
+ printf("datastart: %7d ",dblsb.s_datastart);
+ printf("dataend: %7d ",dblsb.s_dataend);
+ printf("comp: 0x%08x ",dblsb.s_comp);
+ printf("cfaktor: %7d\n",dblsb.s_cfaktor+1);
+ printf("max_cluster: %5d ",dblsb.s_max_cluster);
+ printf("max_cluster2:%5d ",dblsb.s_max_cluster2);
+ printf("cvf_version: %5d ",dblsb.s_cvf_version);
+ printf("free_sec: %7d\n",dblsb.s_free_sectors);
+ if(argc==3)
+ {
+ if(strcmp(argv[2],"dumpcache")==0)
+ { if(ioctl(fd,DMSDOS_DUMPCACHE,w)<0)error();
+ printf("Cache status written to syslog.\n");
+ close(fd);
+ return 0;
+ }
+ if(strcmp(argv[2],"logstat")==0)
+ { if(ioctl(fd,DMSDOS_LOG_STATISTICS,w)<0)error();
+ printf("Statistics written to syslog.\n");
+ close(fd);
+ return 0;
+ }
+ if(strcmp(argv[2],"memory")==0)
+ { if(ioctl(fd,DMSDOS_REPORT_MEMORY,w)<0)error();
+ printf("DMSDOS memory usage (in bytes):\n");
+ printf("Cluster cache: %8ld maximum: ",w[0]);
+ if(w[1]>0)printf("%8ld (estimated)\n",w[1]);
+ else printf(" -unknown- \n");
+ printf("Buffer cache: %8ld maximum: %8ld\n",w[2],w[3]);
+ return 0;
+ }
+
+ }
+
+ if(argc==3||argc==4)
+ {
+ if(strcmp(argv[2],"checkfs")==0)
+ { printf("Please wait while filesystem is checked...\n");
+ w[0]= (argc==4) ? scan(argv[3]) : 0;
+ if(ioctl(fd,DMSDOS_SIMPLE_CHECK,w)<0)error();
+ if(w[0]==1||w[0]==2)printf("Check aborted due to lack of kernel memory.\n");
+ if(w[0]==0)printf("No filesystem error found.\n");
+ if(w[0]==-1)printf("Filesystem has serious errors: FAT level crosslink(s) found.\n");
+ if(w[0]==-2)printf("Filesystem has serious errors: MDFAT level crosslink(s) found.\n");
+ if(w[0]==-3)printf("Filesystem BITFAT mismatches MDFAT.\n");
+ if(w[0]==-1||w[0]==-2||w[0]==-3)
+ { if(ioctl(fd,DMSDOS_SET_COMP,READ_ONLY)<0)error();
+ printf("The filesystem has been set to read-only mode.\n");
+ }
+ close(fd);
+ return 0;
+ }
+ if(strcmp(argv[2],"synccache")==0)
+ { printf("Syncing cluster cache....be patient, this may take some time...\n");
+ if(ioctl(fd,DMSDOS_SYNC_CCACHE,(argc==4) ? scan(argv[3]) : 0)<0)error();
+ printf("Cluster cache synced.\n");
+ close(fd);
+ return 0;
+ }
+
+ }
+
+ if(argc<4)
+ {
+
+ printf("\nPlease wait while filesystem is scanned...\n\n");
+
+ if(ioctl(fd,DMSDOS_EXTRA_STATFS,&dblstat)<0)error();
+
+ printf("free sectors: %7ld ",dblstat.free_sectors);
+ printf("used sectors: %7ld ",dblstat.used_sectors);
+ printf("all sectors: %7ld\n",dblstat.free_sectors+dblstat.used_sectors);
+ printf("max free hole: %7ld ",dblstat.max_hole);
+ i=(100*(dblstat.free_sectors-dblstat.max_hole))/dblstat.free_sectors;
+ printf("fragmentation:%7d%% ",i);
+ i=(100*dblstat.used_sectors)/(dblstat.free_sectors+dblstat.used_sectors);
+ printf("capacity: %7d%%\n",i);
+ printf("free clusters: %7ld ",dblstat.free_clusters);
+ printf("used clusters: %7ld ",dblstat.used_clusters);
+ printf("all clusters: %7ld\n",dblstat.free_clusters+dblstat.used_clusters
+ +dblstat.lost_clusters);
+ printf("compressed: %7ld ",dblstat.compressed_clusters);
+ printf("uncompressed: %7ld ",dblstat.uncompressed_clusters);
+ printf("lost clusters: %7ld\n",dblstat.lost_clusters);
+ printf("cluster compression: %5ld%% ",
+ (dblstat.compressed_clusters+dblstat.uncompressed_clusters==0) ? 0 :
+ (100*dblstat.compressed_clusters)/
+ (dblstat.compressed_clusters+dblstat.uncompressed_clusters)
+ );
+
+ if(dblstat.sectors_lo!=0)
+ ratio=((double)dblstat.sectors_hi)/((double)dblstat.sectors_lo);
+ else ratio=2.0;
+
+ if(dblstat.used_clusters!=0)
+ dosratio=((double)dblstat.used_clusters*dblsb.s_sectperclust)/
+ ((double)dblstat.used_sectors);
+ else dosratio=2.0;
+
+ printf("compression ratio: %5.2f : 1 / %5.2f : 1\n",ratio,dosratio);
+ printf("space allocated by clusters (real allocated space): %7ldKB\n",
+ dblstat.sectors_lo/2);
+ printf("space allocated by clusters (space after decompression): %7ldKB\n",
+ dblstat.sectors_hi/2);
+ printf("compressed free space (estimated free space): %7dKB\n",
+ ((int)(dblstat.free_sectors*ratio))/2);
+ printf("uncompressed free space: %7ldKB\n",
+ dblstat.free_sectors/2);
+ printf("maximum free space due to cluster limit: %7ldKB\n",
+ dblstat.free_clusters*dblsb.s_sectperclust/2);
+
+ if(dblstat.max_hole<=dblsb.s_sectperclust*3&&
+ dblstat.free_sectors>dblsb.s_sectperclust*3)
+ printf("Warning: This CVF should be defragmented at internal MDFAT level.\n");
+ else if(dblstat.free_sectors<=dblsb.s_sectperclust*3||dblsb.s_full==2)
+ printf("Warning: This CVF is full. Do not write to it.\n");
+ else if(dblsb.s_full==1)
+ printf("Warning: This CVF is almost full or highly fragmented at internal MDFAT level.\n");
+ else if(dblstat.free_clusters*dblsb.s_sectperclust<dblstat.free_sectors)
+ {
+ printf("Warning: You cannot use all free space of this CVF due to the cluster limit.\n");
+ /*
+ i=((int)(dblstat.free_sectors*ratio))/dblsb.s_sectperclust
+ -dblstat.free_clusters+dblsb.s_max_cluster;
+ if(i>dblsb.s_max_cluster2)i=dblsb.s_max_cluster2;
+ if(i>dblsb.s_max_cluster)
+ printf(" Enlarge the max_cluster value or the dos compression ratio.\n"
+ " I recommend the following max_cluster value for this CVF: %d\n",i);
+ */
+ printf(" Adapt the compression ratio under Dos.\n");
+ }
+
+ }
+
+ else if(strcmp(argv[2],"bitfat")==0)
+ { w[0]=scan(argv[3]);
+ if(ioctl(fd,DMSDOS_READ_BITFAT,w)<0)error();
+ if(w[1]==0)printf("\nbitfat: sector is free\n");
+ if(((signed long)w[1])>0)printf("\nbitfat: sector is allocated\n");
+ if(((signed long)w[1])<0)printf("\nbitfat: value out of range\n");
+ }
+
+ else if(strcmp(argv[2],"cluster")==0)
+ { w[0]=scan(argv[3]);
+ w[1]=(unsigned long)(&mde);
+ if(ioctl(fd,DMSDOS_READ_MDFAT,w)<0)error();
+ printf("used: %s\n",(mde.flags&2)?"yes":"no");
+ printf("compressed: %s\n",(mde.flags&1)?"no":"yes");
+ printf("flags (raw): 0x%x\n",mde.flags);
+ printf("size uncompressed: %d compressed: %d\n",
+ mde.size_hi_minus_1+1,mde.size_lo_minus_1+1);
+ printf("first sector: %ld\n",mde.sector_minus_1+1);
+ printf("unknown bits: %d\n",mde.unknown);
+ if(ioctl(fd,DMSDOS_READ_DFAT,w)<0)error();
+ printf("next cluster: %ld\n",w[1]);
+ }
+
+ else if(strcmp(argv[2],"rrawcluster")==0)
+ { FILE*f=NULL;
+ if(argc==5)f=fopen(argv[4],"wb");
+ w[0]=scan(argv[3]);
+ w[1]=(unsigned long)(&mde);
+ if(ioctl(fd,DMSDOS_READ_MDFAT,w)<0)error();
+
+ if(mde.flags&2)
+ { int s;
+ for(s=0;s<mde.size_lo_minus_1+1;++s)
+ { buffer.w=s+mde.sector_minus_1+1;
+ if(ioctl(fd,DMSDOS_READ_BLOCK,&buffer)<0)error();
+ for(i=0;i<512;++i)
+ { if(f)fputc(buffer.data[i],f);
+ if(i%16==0){sprintf(a,"%3X : ",i+512*s);strcpy(b,"");}
+ sprintf(c," %02X",buffer.data[i]);
+ strcat(a,c);
+ if(buffer.data[i]>=32&&buffer.data[i]<128)
+ sprintf(c,"%c",buffer.data[i]);
+ else strcpy(c,".");
+ strcat(b,c);
+
+ if(i%16==15)printf("%s %s\n",a,b);
+ }
+ }
+ }
+ else printf("unused cluster, contains no raw data.\n");
+
+ if(f)fclose(f);
+ }
+
+ else if(strcmp(argv[2],"rcluster")==0)
+ { FILE*f=NULL;
+ if(argc==5)f=fopen(argv[4],"wb");
+ w[0]=scan(argv[3]);
+ w[1]=(unsigned long)(&mde);
+ if(ioctl(fd,DMSDOS_READ_MDFAT,w)<0)error();
+ else
+ {
+ struct
+ { unsigned long w;
+ unsigned char data[1];
+ } *buf = malloc(dblsb.s_sectperclust * 512 + 32);
+ if(buf==NULL)
+ { printf("Uhh... unable to get memory...\n");
+ exit(3);
+ }
+ buf->w = w[0];
+ if(ioctl(fd,DMSDOS_READ_CLUSTER,buf)<0)error();
+ for(i=0;i<512*dblsb.s_sectperclust;++i)
+ { if(f)fputc(buf->data[i],f);
+ if(i%16==0){sprintf(a,"%3X : ",i);strcpy(b,"");}
+ sprintf(c," %02X",buf->data[i]);
+ strcat(a,c);
+ if(buf->data[i]>=32&&buf->data[i]<128)
+ sprintf(c,"%c",buf->data[i]);
+ else strcpy(c,".");
+ strcat(b,c);
+ if(i%16==15)printf("%s %s\n",a,b);
+ }
+ }
+ if(f)fclose(f);
+ }
+
+ else if(strcmp(argv[2],"sector")==0)
+ { FILE*f=NULL;
+ if(argc==5)f=fopen(argv[4],"wb");
+ buffer.w=scan(argv[3]);
+ if(ioctl(fd,DMSDOS_READ_BLOCK,&buffer)<0)error();
+
+ for(i=0;i<512;++i)
+ { if(f)fputc(buffer.data[i],f);
+ if(i%16==0){sprintf(a,"%3X : ",i);strcpy(b,"");}
+ sprintf(c," %02X",buffer.data[i]);
+ strcat(a,c);
+ if(buffer.data[i]>=32&&buffer.data[i]<128)
+ sprintf(c,"%c",buffer.data[i]);
+ else strcpy(c,".");
+ strcat(b,c);
+
+ if(i%16==15)printf("%s %s\n",a,b);
+ }
+ if(f)fclose(f);
+ }
+
+ else if(strcmp(argv[2],"setcomp")==0)
+ { ret=0;
+ if(strcmp(argv[3],"ro")==0)ret=ioctl(fd,DMSDOS_SET_COMP,READ_ONLY);
+ else if(strcmp(argv[3],"no")==0)ret=ioctl(fd,DMSDOS_SET_COMP,UNCOMPRESSED);
+ else if(strcmp(argv[3],"guess")==0)ret=ioctl(fd,DMSDOS_SET_COMP,GUESS);
+ else if(strcmp(argv[3],"ds00")==0)ret=ioctl(fd,DMSDOS_SET_COMP,DS_0_0);
+ else if(strcmp(argv[3],"ds01")==0)ret=ioctl(fd,DMSDOS_SET_COMP,DS_0_1);
+ else if(strcmp(argv[3],"ds02")==0)ret=ioctl(fd,DMSDOS_SET_COMP,DS_0_2);
+ else if(strcmp(argv[3],"jm00")==0)ret=ioctl(fd,DMSDOS_SET_COMP,JM_0_0);
+ else if(strcmp(argv[3],"jm01")==0)ret=ioctl(fd,DMSDOS_SET_COMP,JM_0_1);
+ else if(strcmp(argv[3],"sq00")==0)ret=ioctl(fd,DMSDOS_SET_COMP,SQ_0_0);
+ else if(strcmp(argv[3],"sd4")==0)ret=ioctl(fd,DMSDOS_SET_COMP,SD_4);
+ else printf("??? mode %s not recognized.\n",argv[3]);
+ if(ret<0)error();
+ }
+
+ else if(strcmp(argv[2],"setcf")==0)
+ { if(ioctl(fd,DMSDOS_SET_CF,scan(argv[3])-1)<0)error();
+ }
+
+ else if(strcmp(argv[2],"setmaxcluster")==0)
+ { /*if(ioctl(fd,DMSDOS_SET_MAXCLUSTER,scan(argv[3]))<0)error();*/
+ printf("setmaxcluster is depreciated (sorry, it became too problematic).\n"
+ "Please use the tools that came with your CVF package under Dos.\n");
+ }
+
+ else if(strcmp(argv[2],"setloglevel")==0)
+ { if(ioctl(fd,DMSDOS_SET_LOGLEVEL,scan(argv[3]))<0)error();
+ }
+
+ else if(strcmp(argv[2],"setspeedup")==0)
+ { if(ioctl(fd,DMSDOS_SET_SPEEDUP,scan(argv[3]))<0)error();
+ }
+
+ else printf("??? syntax error in command line.\n");
+
+ close(fd);
+ return 0;
+}
diff --git a/src/lib_interface.c b/src/lib_interface.c
new file mode 100644
index 0000000..3ff6796
--- /dev/null
+++ b/src/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_interface.h b/src/lib_interface.h
new file mode 100644
index 0000000..551447b
--- /dev/null
+++ b/src/lib_interface.h
@@ -0,0 +1,189 @@
+/*
+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... :)) */
+#ifdef __linux__
+/* this defines machine-dependent __u8, __s8 etc. types */
+#include<asm/types.h>
+/* this defines get_unaligned and put_unaligned */
+#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
+
+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/listmsg.sh b/src/listmsg.sh
new file mode 100644
index 0000000..cb5d20d
--- /dev/null
+++ b/src/listmsg.sh
@@ -0,0 +1,21 @@
+#!/bin/bash
+
+# This script tries to find all kernel messages in the dmsdos sources.
+# It's not 100% and may print some garbage, but better than nothing.
+
+# Usage:
+# LIST_MESSAGES list all (incl. debug) messages
+# LIST_MESSAGES pattern list only messages with pattern in it
+# LIST_MESSAGES -LOG list normal messages (without debug messages)
+
+if [ "$1" != "" ];
+then
+ if [ "$1" != "-LOG" ];
+ then
+ ( cat dblspace*.c *.c-2.0.33 *.c-2.1.80 dstacker*.c | awk -f remove_comments.awk | grep DMSDOS | grep $1 | cut -f2 -d\" -s | cut -f1 -d\\ | sort -b -d -f | uniq ) 2>/dev/null
+ else
+ ( cat dblspace*.c *.c-2.0.33 *.c-2.1.80 dstacker*.c | awk -f remove_comments.awk | grep DMSDOS | grep -v LOG | cut -f2 -d\" -s | cut -f1 -d\\ | sort -b -d -f | uniq ) 2>/dev/null
+ fi
+else
+( cat dblspace*.c *.c-2.0.33 *.c-2.1.80 dstacker*.c | awk -f remove_comments.awk | grep DMSDOS | cut -f2 -d\" -s | cut -f1 -d\\ | sort -b -d -f | uniq ) 2>/dev/null
+fi
diff --git a/src/mcdmsdos.c b/src/mcdmsdos.c
new file mode 100644
index 0000000..9b02915
--- /dev/null
+++ b/src/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"dmsdos.h"
+#include"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;
+}
diff --git a/src/msdos.fsck-wrapper b/src/msdos.fsck-wrapper
new file mode 100644
index 0000000..cedf1bd
--- /dev/null
+++ b/src/msdos.fsck-wrapper
@@ -0,0 +1,126 @@
+#!/bin/sh
+
+# This is an example shell script that can be used as a wrapper for a
+# msdos.fsck (called by the generic fsck frontend).
+#
+# WARNING: This script is an older implementation. See msdos.fsck-wrapper2
+# for a newer and cleaner one.
+#
+# WARNING: This script needs write access to /tmp which is probably not
+# possible on bootup. Better use msdos.fsck-wrapper2 instead.
+#
+# The script accepts a standard fsck command line and decides upon the
+# the raw filesystem data whether it is a CVF or not. If it is a CVF then
+# dmsdosfsck is invoked.
+#
+# The case when it is not a CVF but an uncompressed msdos partition is a bit
+# more complex. First, dosfsck is invoked to check that dos partition. Then
+# the script tries to mount that partition and scans its root directory for
+# CVFs. If there are CVFs it tries to check them, too, by calling dmsdosfsck
+# on them. After that, the msdos partition is unmounted again in order to
+# restore the previous state. If the -r option is present in the fsck
+# command line, some questions are asked.
+#
+# Note that this script needs a helper program that finds out whether a
+# file is a CVF or not. If you have added the file(1) magics for CVFs to
+# /etc/magic (see the dmsdos installation instructions) you can use a
+# combination of file and grep (for example) for that purpose. I think this
+# is a standard way. If you still prefer the older method (by calling the
+# helper program cvftest) just compile cvftest ('make cvftest') and change
+# some lines below (I've commented out the two cvftest calls and placed a
+# file command in the next line).
+
+###########################################################################
+
+# where to find the different filesystem checker utilities
+DMSDOSFSCK="dmsdosfsck"
+DOSFSCK="dosfsck"
+
+ARGS="$@"
+FILE=""
+ASK=n
+
+while [ "$1" != "" ];
+do
+ case "$1" in
+ -u) shift
+ shift ;;
+ -d) shift
+ shift ;;
+ -r) ASK=y
+ shift ;;
+ -*) shift ;;
+ *) FILE="$1"
+ shift ;;
+ esac;
+done
+
+#echo "ARGS=$ARGS"
+#echo "FILE=$FILE"
+
+#if cvftest $FILE ;
+if [ ! -z "`file $FILE | grep CVF`" ];
+then
+ echo "CVF detected, calling dmsdosfsck..."
+ $DMSDOSFSCK $ARGS
+ CODE="$?"
+else
+ echo "no CVF found, calling dosfsck..."
+ $DOSFSCK $ARGS
+ CODE="$?"
+ if [ "$CODE" != "0" ];
+ then
+ exit $CODE
+ fi
+ if [ $ASK = y ];
+ then
+ echo -n "search $FILE for CVFs in it and check them, too?"
+ read ANS JUNK
+ if [ "$ANS" != "y" -a "$ANS" != "Y" ];
+ then
+ exit 0
+ fi
+ fi
+ mkdir /tmp/fsckwrap.$$
+ if [ "$?" != "0" ];
+ then
+ echo "need write access to /tmp for automatic CVF check (skipped)"
+ exit 0
+ fi
+ chmod 700 /tmp/fsckwrap.$$
+ mount -t msdos $FILE /tmp/fsckwrap.$$
+ if [ "$?" != "0" ];
+ then
+ echo "cannot search $FILE for CVFs in it (skipped)"
+ exit 0
+ fi
+
+ CODE="0"
+ FIRST=y
+ for I in /tmp/fsckwrap.$$/dblspace.0?? /tmp/fsckwrap.$$/drvspace.0?? /tmp/fsckwrap.$$/stacvol.*
+ do
+ if [ -f $I ];
+ then
+ #if cvftest $I ;
+ if [ ! -z "`file $I | grep CVF`" ];
+ then
+ if [ $FIRST = y ];
+ then
+ echo "$FILE contains CVFs"
+ FIRST=n
+ fi
+ echo -n "checking CVF "
+ basename $I
+ $DMSDOSFSCK $ARGS $I
+ if [ "$?" != "0" ];
+ then
+ CODE="$?"
+ fi
+ fi
+ fi
+ done
+ umount /tmp/fsckwrap.$$
+ rmdir /tmp/fsckwrap.$$
+fi
+
+exit $CODE
diff --git a/src/msdos.fsck-wrapper2 b/src/msdos.fsck-wrapper2
new file mode 100644
index 0000000..1f79f55
--- /dev/null
+++ b/src/msdos.fsck-wrapper2
@@ -0,0 +1,101 @@
+#!/bin/sh
+
+# This is an example shell script that can be used as a wrapper for a
+# msdos.fsck (called by the generic fsck frontend).
+#
+# This script is the second version. It uses a new feature of libdmsdos,
+# the built-in direct translation that can access a CVF even if the host
+# filesystem is not mounted. This is now the preferred way.
+#
+# The script accepts a standard fsck command line and decides upon the
+# the raw filesystem data whether it is a CVF or not. If it is a CVF then
+# dmsdosfsck is invoked.
+#
+# The case when it is not a CVF but an uncompressed msdos partition is a bit
+# more complex. First, dosfsck is invoked to check that dos partition. Then
+# the script scans its root directory for CVFs. If there are CVFs it tries
+# to check them, too, by calling dmsdosfsck on them. If the -r option is
+# present in the fsck command line, some questions are asked.
+#
+# Note that this script needs a helper program that finds out whether a
+# file is a CVF or not. If you have added the file(1) magics for CVFs to
+# /etc/magic (see the dmsdos installation instructions) you can use a
+# combination of file and grep (for example) for that purpose. I think this
+# is a standard way. If you still prefer the older method (by calling the
+# helper program cvftest) just compile cvftest ('make cvftest') and change
+# one lines below (I've commented out the cvftest call and placed a
+# file command in the next line).
+
+###########################################################################
+
+# where to find the different filesystem checker utilities
+DMSDOSFSCK="dmsdosfsck"
+DOSFSCK="dosfsck"
+CVFLIST="cvflist"
+
+ARGS="$@"
+FILE=""
+ASK=n
+
+while [ "$1" != "" ];
+do
+ case "$1" in
+ -u) shift
+ shift ;;
+ -d) shift
+ shift ;;
+ -r) ASK=y
+ shift ;;
+ -*) shift ;;
+ *) FILE="$1"
+ shift ;;
+ esac;
+done
+
+#echo "ARGS=$ARGS"
+#echo "FILE=$FILE"
+
+#if cvftest $FILE ;
+if [ ! -z "`file $FILE | grep CVF`" ];
+then
+ echo "CVF detected, calling dmsdosfsck ..."
+ $DMSDOSFSCK $ARGS
+ CODE="$?"
+else
+ echo "no CVF found, calling dosfsck ..."
+ $DOSFSCK $ARGS
+ CODE="$?"
+ if [ "$CODE" != "0" ];
+ then
+ exit $CODE
+ fi
+ if [ $ASK = y ];
+ then
+ echo -n "search $FILE for CVFs in it and check them, too?"
+ read ANS JUNK
+ if [ "$ANS" != "y" -a "$ANS" != "Y" ];
+ then
+ exit 0
+ fi
+ fi
+ CVFS=`$CVFLIST $FILE`
+ if [ -z "$CVFS" ];
+ then
+ echo "no CVFs found."
+ exit 0
+ fi
+
+ CODE="0"
+ for I in $CVFS
+ do
+ I=`echo $I | cut -f2 -d.`
+ echo "checking CVF with ext $I ..."
+ $DMSDOSFSCK $ARGS $FILE:$I
+ if [ "$?" != "0" ];
+ then
+ CODE="$?"
+ fi
+ done
+fi
+
+exit $CODE
diff --git a/src/my_break.h b/src/my_break.h
new file mode 100644
index 0000000..df2bac0
--- /dev/null
+++ b/src/my_break.h
@@ -0,0 +1,30 @@
+#ifndef _MY_BREAK_H
+#define _MY_BREAK_H
+
+#include <linux/sched.h>
+
+#define BREAK_NOW my_break_now(__PRETTY_FUNCTION__);
+
+#define get_esp() ({int a_sp; __asm__ ("mov %%esp,%0":"=r"(a_sp)); a_sp;})
+
+struct wait_queue * my_break_wait=NULL;
+
+static void my_break_cont(void)
+{ wake_up(&my_break_wait);
+}
+
+static void my_break_now(char * s)
+{ unsigned call_pc;
+ unsigned call_sp;
+ call_pc=(typeof(call_pc))__builtin_return_address(0);
+ call_sp=(typeof(call_sp))get_esp();
+ printk ("BREAK : procces %i (current=0x%x) stopped at 0x%x in %s\n",
+ current->pid,(unsigned)current,call_pc,s);
+ printk ("BREAK : sp = 0x%x\n",call_sp);
+ printk ("BREAK : for continue call *0x%x()\n",
+ (unsigned)&my_break_cont);
+ sleep_on(&my_break_wait);
+ printk ("BREAK : continuing\n");
+}
+
+#endif /* _MY_BREAK_H */
diff --git a/src/prepare_dos_8.3 b/src/prepare_dos_8.3
new file mode 100644
index 0000000..f63e3a3
--- /dev/null
+++ b/src/prepare_dos_8.3
@@ -0,0 +1,27 @@
+#!/bin/sh
+
+TARGET_DIR=/tmp/dmsdos/dos8.3/src
+mkdir -p $TARGET_DIR
+
+if [ -f dmsdos-config.h ];
+then
+ sh putdos dmsdos-config.h $TARGET_DIR
+else
+ sh putdos dmsdos-config.h.default $TARGET_DIR
+fi
+sh putdos dmsdos.h $TARGET_DIR
+sh putdos lib_interface.h $TARGET_DIR
+sh putdos lib_interface.c $TARGET_DIR
+sh putdos dblspace_interface.c $TARGET_DIR
+sh putdos dblspace_dec.c $TARGET_DIR
+sh putdos dblspace_compr.c $TARGET_DIR
+sh putdos dblspace_methsq.c $TARGET_DIR
+sh putdos dblspace_alloc.c $TARGET_DIR
+sh putdos dblspace_chk.c $TARGET_DIR
+sh putdos dblspace_tables.c $TARGET_DIR
+sh putdos dstacker_compr.c $TARGET_DIR
+sh putdos dstacker_dec.c $TARGET_DIR
+sh putdos dstacker_alloc.c $TARGET_DIR
+sh putdos dcread.c $TARGET_DIR
+sh putdos mcdmsdos.c $TARGET_DIR
+sh putdos dmsdosfsck.c $TARGET_DIR
diff --git a/src/putdos b/src/putdos
new file mode 100644
index 0000000..2a8f2cb
--- /dev/null
+++ b/src/putdos
@@ -0,0 +1,18 @@
+#!/bin/sh
+
+# create temporary command
+
+FILTER=`sh putdos_helper < putdos.cfg`
+
+#echo "FILTER=$FILTER"
+
+echo "#!/bin/sh" > filter.sh
+echo "$FILTER" >> filter.sh
+
+RFN=`echo $1 | sh filter.sh`
+
+echo "translating $1 to $2/$RFN ..."
+
+cat $1 | sh filter.sh > $2/$RFN
+
+rm filter.sh
diff --git a/src/putdos.cfg b/src/putdos.cfg
new file mode 100644
index 0000000..e35d8ef
--- /dev/null
+++ b/src/putdos.cfg
@@ -0,0 +1,19 @@
+dmsdos-config.h d-config.h
+dmsdos-config.h.default d-config.h
+dmsdos.h dmsdos.h
+lib_interface.h l_interf.h
+lib_interface.c l_interf.c
+dblspace_interface.c d_interf.c
+dblspace_dec.c d_dec.c
+dblspace_compr.c d_compr.c
+dblspace_methsq.c d_methsq.c
+dblspace_alloc.c d_alloc.c
+dblspace_chk.c d_chk.c
+dblspace_tables.c d_tables.c
+dstacker_compr.c s_compr.c
+dstacker_dec.c s_dec.c
+dstacker_alloc.c s_alloc.c
+dcread.c dcread.c
+mcdmsdos.c mcdmsdos.c
+dmsdosfsck.c dfsck.c
+Makefile.dos-8.3 makefile.dos
diff --git a/src/putdos_helper b/src/putdos_helper
new file mode 100644
index 0000000..52580ca
--- /dev/null
+++ b/src/putdos_helper
@@ -0,0 +1,14 @@
+#!/bin/sh
+
+FIRST=y
+read A B
+while [ "$A" != "" -a "$B" != "" ];
+do
+if [ "$FIRST" = "n" ];
+then
+echo -n "|"
+fi
+echo -n "sed s/$A/$B/g"
+read A B
+FIRST=n
+done
diff --git a/src/remove_comments.awk b/src/remove_comments.awk
new file mode 100644
index 0000000..74d14e5
--- /dev/null
+++ b/src/remove_comments.awk
@@ -0,0 +1,36 @@
+#
+# This is an awk script which removes comments in c files.
+# it does not follow #include directives.
+#
+
+BEGIN{
+ incomment=0
+}
+
+# eliminate comments
+{
+ # remove all comments fully contained on a single line
+ gsub("\\/\\*.*\\*\\/", "")
+ if (incomment) {
+ if ($0 ~ /\*\//) {
+ incomment = 0;
+ gsub(".*\\*\\/", "")
+ } else {
+ next
+ }
+ } else {
+ # start of multi-line comment
+ if ($0 ~ /\/\*/)
+ {
+ incomment = 1;
+ sub("\\/\\*.*", "")
+ } else if ($0 ~ /\*\//) {
+ incomment = 0;
+ sub(".*\\*\\/", "")
+ }
+ }
+ print $0
+}
+
+END{
+}
diff --git a/src/win32_msc50.bat b/src/win32_msc50.bat
new file mode 100644
index 0000000..878ce57
--- /dev/null
+++ b/src/win32_msc50.bat
@@ -0,0 +1,65 @@
+@echo off
+rem this file compiles dmsdos under win32. sorry I did not manage to get a
+rem makefile work with nmake :(
+rem
+rem I had success with visual c++ 5.0 from microsoft
+
+rem please adapt the include and library paths here if necessary
+rem set CFLAGS=-I d:\programme\devstudio\vc\include -D__DMSDOS_LIB__ -c
+rem set LFLAGS=/libpath:d:\programme\devstudio\vc\lib
+set CFLAGS=-D__DMSDOS_LIB__ -DUSE_SOPEN -c
+set LFLAGS=
+
+rem check for existing configuration file
+if exist dmsdos-config.h goto isthere
+copy dmsdos-config.h.default dmsdos-config.h
+:isthere
+
+del *.obj
+
+cl %CFLAGS% lib_interface.c
+cl %CFLAGS% dblspace_interface.c
+cl %CFLAGS% dblspace_dec.c
+cl %CFLAGS% dblspace_compr.c
+cl %CFLAGS% dblspace_methsq.c
+cl %CFLAGS% dblspace_alloc.c
+cl %CFLAGS% dblspace_chk.c
+cl %CFLAGS% dblspace_tables.c
+cl %CFLAGS% dstacker_compr.c
+cl %CFLAGS% dstacker_dec.c
+cl %CFLAGS% dstacker_alloc.c
+
+rem dmsdos_library.lo: $(LIB_OBJS)
+rem ld -r -o dmsdos_library.lo $^
+rem we don't want this here
+
+rem libdmsdos.a: dmsdos_library.lo
+rem ar rcs libdmsdos.a dmsdos_library.lo
+
+del libdmsdos.lib
+lib /out:libdmsdos.lib lib_interface.obj dblspace_interface.obj dblspace_dec.obj dblspace_compr.obj dblspace_methsq.obj dblspace_alloc.obj dblspace_chk.obj dblspace_tables.obj dstacker_compr.obj dstacker_dec.obj dstacker_alloc.obj
+
+rem
+rem libdmsdos.so: dmsdos_library.lo
+rem ld -shared -o libdmsdos.so dmsdos_library.lo
+rem
+
+rem dcread: dcread.c libdmsdos.a dmsdos.h dmsdos-config.h
+rem $(CC) -Wall -ggdb -o dcread dcread.c -L. -ldmsdos
+cl %CFLAGS% dcread.c
+link %LFLAGS% dcread.obj libdmsdos.lib
+
+rem
+rem mcdmsdos: mcdmsdos.c libdmsdos.a dmsdos.h dmsdos-config.h
+rem $(CC) -Wall -ggdb -o mcdmsdos mcdmsdos.c -L. -ldmsdos
+
+cl %CFLAGS% mcdmsdos.c
+link %LFLAGS% mcdmsdos.obj libdmsdos.lib
+
+rem
+rem dmsdosfsck: dmsdosfsck.c libdmsdos.a dmsdos.h dmsdos-config.h
+rem $(CC) -Wall -o dmsdosfsck dmsdosfsck.c -L. -ldmsdos
+
+rem cl %CFLAGS% dmsdosfsck.c
+rem link %LFLAGS% dmsdosfsck.obj libdmsdos.lib
+rem this does not compile due to missing sleep ARGHH... use Unix, Win32 sucks.