diff options
author | cc4b05f61e2d8f77114750386c9f9a60 <cc4b05f61e2d8f7@7114750386c9f9a60> | 2023-05-11 14:38:47 +0000 |
---|---|---|
committer | cc4b05f61e2d8f77114750386c9f9a60 <cc4b05f61e2d8f7@7114750386c9f9a60> | 2023-05-11 14:38:47 +0000 |
commit | f5450bfd35a6410528d124f534c2b1a958cafe51 (patch) | |
tree | a808b12d6ad5343fabdec7b8918df6b4d844e03f /src | |
parent | 5ad2bb7a6ac7e97c031908d2439808a00fff6214 (diff) | |
download | dmsdosnow-dmsdos-0.9.2.2.tar.gz |
dmsdos-0.9.2.2 addeddmsdos-0.9.2.2
Diffstat (limited to 'src')
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&§ornr<=dblsb->s_dataend-size) + { /* search exactly fitting hole near sectornr */ + testsek=sectornr; + while(testsek<sectornr+NEAR_AREA) + { if(dbl_bitfat_value(sb,testsek,NULL)) + { ++testsek; + continue; + } + i=1; + while(i<=size&&dbl_bitfat_value(sb,testsek+i,NULL)==0)++i; + if(i==size) + { dblsb->s_full=0; + ++nearfound; + dblsb->s_lastnear=testsek; + return testsek; + } + testsek+=i; + } + testsek=sectornr; + while(testsek>sectornr-NEAR_AREA) + { if(dbl_bitfat_value(sb,testsek,NULL)) + { --testsek; + continue; + } + i=1; + while(i<=size&&dbl_bitfat_value(sb,testsek-i,NULL)==0)++i; + if(i==size) + { dblsb->s_full=0; + ++nearfound; + dblsb->s_lastnear=testsek-i+1; + return testsek-i+1; + } + testsek-=i; + } + } + /* not found, continue */ +} /* end of old strategy */ + + /* search for a big hole */ + if(dblsb->s_lastbig==-1)goto nobighole; + + testsek=dblsb->s_lastbig; + if(testsek<dblsb->s_datastart||testsek+size>dblsb->s_dataend) + testsek=dblsb->s_datastart; + + while(testsek<=dblsb->s_dataend-size) + { if(dbl_bitfat_value(sb,testsek,NULL)) + { ++testsek; + continue; + } + i=1; + while(i<BIG_HOLE&&dbl_bitfat_value(sb,testsek+i,NULL)==0)++i; + if(i==BIG_HOLE) + { dblsb->s_full=0; + ++bigfound; + dblsb->s_lastbig=testsek; + return testsek; + } + testsek+=i; + } + + if(dblsb->s_lastbig==0) + dblsb->s_lastbig=-1; /*there's no big hole any more*/ + else + dblsb->s_lastbig=0; /* next time try from the beginning */ + +nobighole: + +if((dmsdos_speedup&SP_NO_EXACT_SEARCH)==0) +{ + /* search for an exactly fitting hole */ + /* hmmm... now the search code becomes awfully slow */ + testsek=dblsb->s_datastart; + while(testsek<=dblsb->s_dataend-size) + { if(dbl_bitfat_value(sb,testsek,NULL)) + { ++testsek; + continue; + } + i=1; + while(i<=size&&dbl_bitfat_value(sb,testsek+i,NULL)==0)++i; + if(i==size) + { dblsb->s_full=0; + ++exactfound; + return testsek; + } + testsek+=i; + } +} + + if(dblsb->s_full==0) + { printk(KERN_CRIT "DMSDOS: CVF almost full or highly fragmented at MDFAT level.\n"); + dblsb->s_full=1; + } + +tryany: + /* last trial: search for any hole >= size */ + testsek=dblsb->s_datastart; + while(testsek<=dblsb->s_dataend-size) + { if(dbl_bitfat_value(sb,testsek,NULL)) + { ++testsek; + continue; + } + i=1; + while(i<size&&dbl_bitfat_value(sb,testsek+i,NULL)==0)++i; + if(i==size) + { ++anyfound; + return testsek; + } + testsek+=i; + } + + /* not found, means disk full or MDFAT too fragmented */ + ++notfound; + + if(dblsb->s_cvf_version==DRVSP3) + { if(dblsb->s_full==0) + { printk(KERN_CRIT "DMSDOS: CVF almost full or highly fragmented at MDFAT level.\n"); + dblsb->s_full=1; + } + } + else /* this is for CVFs that cannot fragment cluster data */ + { if(dblsb->s_full<2) + printk(KERN_CRIT "DMSDOS: CVF full or too fragmented at MDFAT level.\n"); + dblsb->s_full=2; + } + return 0; +} + +void log_found_statistics() +{ printk(KERN_INFO "DMSDOS: free sector finding statistics:\n"); + printk(KERN_INFO "nearfound=%d bigfound=%d exactfound=%d anyfound=%d fragfound=%d notfound=%d\n", + nearfound,bigfound,exactfound,anyfound,fragfound,notfound); +} + +#ifdef DMSDOS_CONFIG_DRVSP3 +int try_fragmented(struct super_block*sb,int anear,int nr, + unsigned char*fraglist) +{ + int i; + int sector=anear; + Dblsb*dblsb=MSDOS_SB(sb)->private_data; + int again=1; + int frags; + int cnt; + + /* if you never want dmsdos to write fragmented clusters as a last resort + then uncomment the next return statement */ + + /* return -ENOSPC; */ + + if(dblsb->s_free_sectors<nr) + { if(dblsb->s_full<2)printk(KERN_CRIT "DMSDOS: CVF full.\n"); + dblsb->s_full=2; + return -ENOSPC; + } + + printk(KERN_DEBUG "DMSDOS: trying to allocate fragmented space...\n"); + LOG_ALLOC("DMSDOS: try_fragmented: start, anear=%d nr=%d\n",anear,nr); + + if(anear==0)sector=dblsb->s_lastnear; + + if(sector<dblsb->s_datastart||sector>dblsb->s_dataend) + { sector=dblsb->s_datastart; + again=0; + } + + retry: + frags=0; + fraglist[0]=0; + fraglist[1]=0; + fraglist[2]=0; + fraglist[3]=0; + cnt=nr; + + while(cnt>0&§or<=dblsb->s_dataend) + { if(dbl_bitfat_value(sb,sector,NULL)) + { ++sector; + continue; + } + /* free sector found */ + i=1; + while(dbl_bitfat_value(sb,sector+i,NULL)==0&&i<cnt)++i; + /* i=number of free sectors :) */ + ++frags; + fraglist[frags*4]=sector-1; + fraglist[frags*4+1]=(sector-1)>>8; + fraglist[frags*4+2]=(sector-1)>>16; + fraglist[frags*4+3]=(sector-1)>>24; + fraglist[frags*4+3]|=(i-1)<<2; + fraglist[0]=frags; + sector+=i+1; + cnt-=i; + } + if(cnt>0&&again!=0) + { sector=dblsb->s_datastart; + again=0; + goto retry; + } + + /* now evaluate the result, check for strange things */ + if(cnt>0) + { if(dblsb->s_full<2) + printk(KERN_CRIT "DMSDOS: CVF full (cannot even allocate fragmented space)\n"); + dblsb->s_full=2; + return -ENOSPC; + } + if(cnt<0) + { printk(KERN_ERR "DMSDOS: try_fragmented: cnt<0 ? This is a bug.\n"); + return -EIO; + } + if(frags<2||frags>dblsb->s_sectperclust+1) + { printk(KERN_ERR "DMSDOS: try_fragmented: frags=%d ? Cannot happen.\n",frags); + return -EIO; + } + + /* correct statistics */ + ++fragfound;--notfound; + dblsb->s_lastnear=sector; + dblsb->s_full=1; /* uhh... 0 might be dangerous... */ + + /* fraglist must be written to disk in *any* case in order to + still represent a correct filesystem + this is handled by dbl_write_cluster to prevent too much overhead */ + + LOG_ALLOC("DMSDOS: try_fragmented: success, frags=%d\n",frags); + return 0; +} +#endif /* DMSDOS_CONFIG_DRVSP3 */ + +#ifdef DMSDOS_CONFIG_DBL +/* replaces an existing cluster; + this unusual function must be called before rewriting any file cluster; + *** size must be known (encoded in mde) *** + if fraglist!=NULL fragmented clusters are allowed for drivespace 3 + returns first sector nr + changes mde and fraglist +*/ + +#define MAX_UNDO_LIST 70 + +int dbl_replace_existing_cluster(struct super_block*sb, int cluster, + int near_sector, + Mdfat_entry*mde, + unsigned char*fraglist) +{ Mdfat_entry old_mde,dummy; + int i; + int newval; + int sector; + int old_sector; + int old_size; + int new_size; + unsigned long undo_list[MAX_UNDO_LIST]; + Dblsb*dblsb=MSDOS_SB(sb)->private_data; + + lock_mdfat_alloc(dblsb); + + LOG_ALLOC("DMSDOS: dbl_replace_existing_cluster cluster=%d near_sector=%d\n", + cluster,near_sector); + dbl_mdfat_value(sb,cluster,NULL,&old_mde); + old_size=old_mde.size_lo_minus_1+1; + old_sector=old_mde.sector_minus_1+1; + new_size=mde->size_lo_minus_1+1; + mde->unknown=0; /* ensure fragmented bit is clear */ + if(old_mde.flags&2) + { + /* test whether same length (and not fragmented in drivespace 3) */ + if(old_size==new_size&& + (dblsb->s_cvf_version!=DRVSP3||(old_mde.unknown&2)==0) + ) + { LOG_ALLOC("DMSDOS: dbl_replace_existing_cluster: same length, ok\n"); + sector=old_sector; + goto mdfat_update; + } + if(dblsb->s_cvf_version==DRVSP3&&(old_mde.unknown&2)!=0&&fraglist!=NULL) + { /*old cluster is fragmented and new *is allowed* to be fragmentd */ + struct buffer_head*bh=raw_bread(sb,old_sector); + if(bh) + { int fragcount; + int cnt; + int sec; + int fragpnt; + int sects; + int m_cnt=0; + int m_sec=0; + + fragcount=bh->b_data[0]; + sects=0; + if(fragcount<2||fragcount>dblsb->s_sectperclust+1|| + bh->b_data[1]!=0||bh->b_data[2]!=0||bh->b_data[3]!=0 + ) + { raw_brelse(sb,bh); + goto check_failed; + } + + for(fragpnt=1;fragpnt<=fragcount;++fragpnt) + { cnt=bh->b_data[fragpnt*4+3]; + cnt&=0xff; + cnt/=4; + cnt+=1; + sec=bh->b_data[fragpnt*4]; + sec&=0xff; + sec+=bh->b_data[fragpnt*4+1]<<8; + sec&=0xffff; + sec+=bh->b_data[fragpnt*4+2]<<16; + sec&=0xffffff; + sec+=1; + + if(fragpnt==1) + { m_cnt=cnt; + m_sec=sec; + if(sec!=old_mde.sector_minus_1+1||cnt!=old_mde.size_lo_minus_1+1) + { printk(KERN_ERR "DMSDOS: dbl_replace_existing_cluster: checking old fraglist: first fragment wrong in cluster %d\n", + cluster); + raw_brelse(sb,bh); + goto check_failed; + } + } + + sects+=cnt; + } + raw_brelse(sb,bh); + if(sects-1/*subtract space for fraglist*/==new_size) + { /* we can reuse it */ + memcpy(fraglist,bh->b_data,4*(fragcount+1)); + mde->unknown|=2; + mde->size_lo_minus_1=m_cnt-1; + sector=m_sec; + LOG_ALLOC("DMSDOS: dbl_replace_existing_cluster: same fragmented size, ok.\n"); + goto mdfat_update; + } + check_failed: ; /*Win32 compiler wants a semicolon here */ + /* fall through */ + } + } + /* different length, replace mdfat entry */ + newval=0; + LOG_ALLOC("DMSDOS: dbl_replace_existing_cluster: freeing old sectors...\n"); + u_free_cluster_sectors(sb,cluster,undo_list); + LOG_ALLOC("DMSDOS: dbl_replace_existing_cluster: freeing finished\n"); + } + LOG_ALLOC("DMSDOS: dbl_replace_existing_cluster: call find_free_bitfat...\n"); + sector=find_free_bitfat(sb,near_sector,new_size); + LOG_ALLOC("DMSDOS: dbl_replace_existing_cluster: find_free_bitfat returned %d\n", + sector); + if(sector<=0) + { +#ifdef DMSDOS_CONFIG_DRVSP3 + if(dblsb->s_cvf_version==DRVSP3&&fraglist!=NULL + &&(dmsdos_speedup&SP_NO_FRAG_WRITE)==0) + { i=try_fragmented(sb,near_sector,new_size+1,fraglist);/*yes one sector more*/ + if(i==0) /* success */ + { /* scan fraglist */ + int frags; + int seccount; + int usector; + int j; + + frags=fraglist[0]; + for(i=1;i<=frags;++i) + { seccount=fraglist[i*4+3]; + seccount&=0xff; + seccount/=4; + seccount+=1; + usector=fraglist[i*4]; + usector&=0xff; + usector+=fraglist[i*4+1]<<8; + usector&=0xffff; + usector+=fraglist[i*4+2]<<16; + usector&=0xffffff; + usector+=1; + + if(i==1) /* note values for mdfat */ + { mde->size_lo_minus_1=seccount-1; + sector=usector; + } + + /* allocate in bitfat */ + newval=1; + for(j=0;j<seccount;++j) + { /* check whether sectors are really free */ + if(dbl_bitfat_value(sb,usector+j,NULL)) + { printk(KERN_EMERG "DMSDOS: try_fragmented returned non-free sectors!\n"); + /* WARNING: bitfat is corrupt now */ + unlock_mdfat_alloc(dblsb); + panic("DMSDOS: dbl_replace_existing_cluster: This is a bug - reboot and check filesystem\n"); + return -EIO; + } + dbl_bitfat_value(sb,usector+j,&newval); + } + } + mde->unknown|=2; /* set fragmented bit */ + goto mdfat_update; + } + /* try_fragmented failed: fall through */ + } +#endif /* DMSDOS_CONFIG_DRVSP3 */ + if(old_mde.flags&2) + { /* undo bitfat free */ + LOG_ALLOC("DMSDOS: dbl_replace_existing_cluster: undoing bitfat free...\n"); + newval=1; + for(i=0;undo_list[i]!=0;++i) + dbl_bitfat_value(sb,undo_list[i],&newval); + } + unlock_mdfat_alloc(dblsb); + return -ENOSPC; /* disk full */ + } + /* check whether really free (bug supposed in find_free_bitfat) */ + for(i=0;i<new_size;++i) + { if(dbl_bitfat_value(sb,sector+i,NULL)) + { printk(KERN_EMERG "DMSDOS: find_free_bitfat returned sector %d size %d but they are not all free!\n", + sector,new_size); + unlock_mdfat_alloc(dblsb); + panic("DMSDOS: dbl_replace_existing_cluster: This is a bug - reboot and check filesystem\n"); + return -EIO; + } + } + newval=1; + LOG_ALLOC("DMSDOS: dbl_replace_existing_cluster: allocating in bitfat...\n"); + for(i=0;i<new_size;++i)dbl_bitfat_value(sb,sector+i,&newval); + +mdfat_update: + mde->sector_minus_1=sector-1; + mde->flags|=2; + LOG_ALLOC("DMSDOS: dbl_replace_existing_cluster: writing mdfat...\n"); + dbl_mdfat_value(sb,cluster,mde,&dummy); + unlock_mdfat_alloc(dblsb); + return sector; /* okay */ +} +#endif diff --git a/src/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(§or,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(§or,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(§or,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(§or,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.
|